Variable to return or return directly? - c

I'm learning to program and sometimes I find that using a variable to return makes my code more readable.
I was wondering if these functions perform the same operations and are equally efficient.
CASE 1:
int Foo1()
{
int x = 5 + 6 + 7; // Return variable
return x;
}
int Foo2(int y)
{
return 5 + 6 + 7;
}
In this case I think that the initialization and sum occur at compile time so there's no difference between them.
CASE 2:
int Foo1(int y)
{
int x = y + 6 + 7; // Return variable
return x;
}
int Foo2(int y)
{
return y + 6 + 7;
}
But, what happen in this case? It seems that the initialization occur at execution time and it has to perform it.
Is returning the value directly faster than initialize a variable and then returning it? Should I always try to return values directly instead using a variable to return?

You can easily try this yourself.
You can get the assembly from your compiler
Without optimization:
(gcc -S -O0 -o src.S src.c)
.file "so_temp.c"
.text
.globl case1Foo1
.type case1Foo1, #function
case1Foo1:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $18, -4(%rbp)
movl -4(%rbp), %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size case1Foo1, .-case1Foo1
.globl case1Foo2
.type case1Foo2, #function
case1Foo2:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $18, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size case1Foo2, .-case1Foo2
.globl case2Foo1
.type case2Foo1, #function
case2Foo1:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -20(%rbp)
movl -20(%rbp), %eax
addl $13, %eax
movl %eax, -4(%rbp)
movl -4(%rbp), %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size case2Foo1, .-case2Foo1
.globl case2Foo2
.type case2Foo2, #function
case2Foo2:
.LFB3:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
addl $13, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE3:
.size case2Foo2, .-case2Foo2
.ident "GCC: (Ubuntu 8.3.0-6ubuntu1) 8.3.0"
.section .note.GNU-stack,"",#progbits
Ther you can see, that the foo2 versions have a few instructions less than the foo1 versions of the functions.
With optimization turned to O3:
(gcc -S -O3 -o src.S src.c)
.file "so_temp.c"
.text
.p2align 4,,15
.globl case1Foo1
.type case1Foo1, #function
case1Foo1:
.LFB0:
.cfi_startproc
movl $18, %eax
ret
.cfi_endproc
.LFE0:
.size case1Foo1, .-case1Foo1
.p2align 4,,15
.globl case1Foo2
.type case1Foo2, #function
case1Foo2:
.LFB5:
.cfi_startproc
movl $18, %eax
ret
.cfi_endproc
.LFE5:
.size case1Foo2, .-case1Foo2
.p2align 4,,15
.globl case2Foo1
.type case2Foo1, #function
case2Foo1:
.LFB2:
.cfi_startproc
leal 13(%rdi), %eax
ret
.cfi_endproc
.LFE2:
.size case2Foo1, .-case2Foo1
.p2align 4,,15
.globl case2Foo2
.type case2Foo2, #function
case2Foo2:
.LFB7:
.cfi_startproc
leal 13(%rdi), %eax
ret
.cfi_endproc
.LFE7:
.size case2Foo2, .-case2Foo2
.ident "GCC: (Ubuntu 8.3.0-6ubuntu1) 8.3.0"
.section .note.GNU-stack,"",#progbits
both versions are exactly the same.
Still I don't think that this is something you should optimize yourself.
In this case readable code should be preferred, especially as code normally isn't compiled with optimizations turned off.

Case 2 is more efficient, but is often not needed as the compiler is extremely likely to optimize case 1 into case 2.
Go for readability if it doesn't hurt performance (as in this case).

Any compiler of at least modest quality will, at even low levels of optimization (such as GCC’s -O1), compile these to the same code. For the most part, any correct optimization you can easily see will be performed by a good compiler.
The C standard does not require compilers to mindlessly compile code into instructions that perform the exact steps in the C source code. It only requires compilers to produce code that has the same effects. Those effects are defined in terms of observable behavior, which includes the output of the program, interactions with the user, and access to volatile objects (special objects you will learn about later). Compilers will eliminate things like intermediate variables as long as they can do so without changing the observable behavior.

Related

Does necessary content of same header will be duplicated in produced executable?

I use gcc. We know the cpp expands all macros definitions and include statements and passes the result to the actual compiler to create an executable file. I tested the result of cpp and see that some parts of header which are necessary, are included in output of cpp for each source file.
But I want to know if in a project I have a header that is included in multiple source files, then does the related content to that header will be duplicated multiple times in produced executable? Or there will be multiple shortcuts to them? If there will be shortcuts, I want to know why cpp replaces shortcuts of source files with more code and pass the result to cc1?
For example in a header, I have a function with name kids
int kids(){
return 1;
}
and call it in test.c source file. The cpp puts the definition of kids in its output. But in compiled file (test.s which is the result of cc -S test.c), I only see call kids which is replaced instead of function definition. I call that a shortcut.I want to know what will happen for that function and its calls in executable?
Edit:
The assembly off test.c is:
.file "test.c"
.text
.globl kids
.type kids, #function
kids:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $1, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size kids, .-kids
.section .rodata
.LC0:
.string "C Rocks + Ali!"
.LC1:
.string "%d"
.text
.globl main
.type main, #function
main:
.LFB1:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .LC0(%rip), %rdi
call puts#PLT
movl $0, %eax
call kids
movl %eax, %esi
leaq .LC1(%rip), %rdi
movl $0, %eax
call printf#PLT
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size main, .-main
.ident "GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0"
.section .note.GNU-stack,"",#progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:

How to run converted .asm code from .c using 'gcc' in Emu8086

I am new here and I converted code from C language to asm. However, it doesn't look like normal code in asm language. So my question is how can I convert a code from C(or C++) language to Assembly language, that the converted asm code could be run on Emu8086.
Here is a simple c code:
#include<stdio.h>
void Hello(){
printf("Hello world");
}
int main (){
Hello();
return 0;
}
Then I converted it with gcc -S test.c and here is the answer:
.file "test1.c"
.section .rodata
.LC0:
.string "Hello world"
.text
.globl Hello
.type Hello, #function
Hello:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .LC0(%rip), %rdi
movl $0, %eax
call printf#PLT
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size Hello, .-Hello
.globl main
.type main, #function
main:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $0, %eax
call Hello
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size main, .-main
.ident "GCC: (Debian 6.3.0-18+deb9u1) 6.3.0 20170516"
.section .note.GNU-stack,"",#progbits
Emu8086 does what it says on the tin: it emulates an Intel 8086 processor. The assembly that GCC has produced is for your host machine (since you haven't told it to do otherwise), which evidently uses an x86-64 instructions set. The 8086 can't understand most of these instructions. You need to cross-compile it to an x86 16-bit real-mode executable. The -m16 option on GCC will generate 16-bit code, but it apparently still uses 32-bit registers (EAX, etc.). So you will have to find a compiler that targets the basic 8086 instruction set.

Putting a local array changes assembly output for X86-64

When we have just an int variable in main:
int main() {
int d;
return 0;
}
Following code is generated for x86-64 on Linux by gcc -S test.c.
.file "test.c"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 6.3.1 20170109"
.section .note.GNU-stack,"",#progbits
Putting an array as local variable
int main() {
int d[2];
return 0;
}
generates a lot of extraneous code at the beginning which I am not able to comprehend.
.file "test.c"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq -4144(%rsp), %rsp
orq $0, (%rsp)
leaq 4128(%rsp), %rsp
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
movl $0, %eax
movq -8(%rbp), %rdx
xorq %fs:40, %rdx
je .L3
call __stack_chk_fail#PLT
.L3:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 6.3.1 20170109"
.section .note.GNU-stack,"",#progbits
Specifically, what are these instructions doing?
leaq -4144(%rsp), %rsp
orq $0, (%rsp)
leaq 4128(%rsp), %rsp
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
movl $0, %eax

Why does GCC not add .section into the assembly

If you look at the second line of this program it just says ".text". When I write assembly programs I though that you had to put ".section .text" Why does GCC omit the ".section". I also noticed that it includes it before declaring rodata bellow ".section .rodata".
Also just wondering what ".type sum, #function" does? I wrote an assembly function this morning without it and it executed fine.
.file "test.c"
.text
.globl sum
.type sum, #function
sum:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movss %xmm0, -4(%rbp)
movss %xmm1, -8(%rbp)
movss -4(%rbp), %xmm0
mulss -8(%rbp), %xmm0
cvttss2si %xmm0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size sum, .-sum
.section .rodata
.LC2:
.string "%d\n"
.text
.globl main
.type main, #function
main:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movss .LC0(%rip), %xmm1
movss .LC1(%rip), %xmm0
call sum
movl %eax, -4(%rbp)
movl -4(%rbp), %eax
movl %eax, %esi
movl $.LC2, %edi
movl $0, %eax
call printf
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size main, .-main
.section .rodata
.align 4
.LC0:
.long 1092930765
.align 4
.LC1:
.long 1092825907
.ident "GCC: (Ubuntu 4.9.2-10ubuntu13) 4.9.2"
.section .note.GNU-stack,"",#progbits
Collecting up some comments into an answer:
Before arbitrary section names were possible, .text, .data, and .bss were assembler directives. Now, you can write .section .text instead. This should all be documented in the GNU as manual. (linked to latest version).
.type sum, #function
sets some ELF symbol-type stuff. IDK if this matters for dynamic linking, but it doesn't for static linkage. There's a lot of stuff the compiler emits but that you don't actually need for your code to run. This is not a bad thing.
For the other things in gcc asm output, have a look at my answer to GCC Assembly Optimizations - Why are these equivalent?

Atomic load and store functions produce same assembly code as non-atomic load and store

Why is the assembly output of store_idx_x86() the same as store_idx() and load_idx_x86() the same as load_idx()?
It was my understanding that __atomic_load_n() would flush the core's invalidation queue, and __atomic_store_n() would flush the core's store buffer.
Note -- I complied with: gcc (GCC) 4.8.2 20140120 (Red Hat 4.8.2-16)
Update: I understand that x86 will never reorder stores with other stores and loads with other loads -- so is gcc smart enough to implement sfence and lfence only when it is needed or should using __atomic_ result in a fence (assuming a memory model stricter than __ATOMIC_RELAXED)?
Code
#include <stdint.h>
inline void store_idx_x86(uint64_t* dest, uint64_t idx)
{
*dest = idx;
}
inline void store_idx(uint64_t* dest, uint64_t idx)
{
__atomic_store_n(dest, idx, __ATOMIC_RELEASE);
}
inline uint64_t load_idx_x86(uint64_t* source)
{
return *source;
}
inline uint64_t load_idx(uint64_t* source)
{
return __atomic_load_n(source, __ATOMIC_ACQUIRE);
}
Assembly:
.file "util.c"
.text
.globl store_idx_x86
.type store_idx_x86, #function
store_idx_x86:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -8(%rbp), %rax
movq -16(%rbp), %rdx
movq %rdx, (%rax)
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size store_idx_x86, .-store_idx_x86
.globl store_idx
.type store_idx, #function
store_idx:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -8(%rbp), %rax
movq -16(%rbp), %rdx
movq %rdx, (%rax)
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size store_idx, .-store_idx
.globl load_idx_x86
.type load_idx_x86, #function
load_idx_x86:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movq (%rax), %rax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size load_idx_x86, .-load_idx_x86
.globl load_idx
.type load_idx, #function
load_idx:
.LFB3:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movq (%rax), %rax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE3:
.size load_idx, .-load_idx
.ident "GCC: (GNU) 4.8.2 20140120 (Red Hat 4.8.2-16)"
.section .note.GNU-stack,"",#progbits
Why is the assembly output of store_idx_x86() the same as store_idx() and load_idx_x86() the same as load_idx()?
On x86, assuming compiler-enforced alignment, they are the same operations. Loads and Stores to aligned addresses of the native size or smaller are guaranteed to be atomic. Reference Intel manual vol 3A, 8.1.1:
The Pentium processor (and newer processors since) guarantees that the following additional memory operations
will always be carried out atomically: Reading or writing a quadword aligned on a 64-bit boundary [...]
Furthermore, x86 enforces a strongly ordered memory model, meaning every store and load has implicit release and acquire semantics, respectively.
Lastly, the fencing instructions you mention are only required when using Intel's non-temporal SSE instructions (great reference here), or when needing to create a store-load fence (article here) (and that one is the mfence or lock instruction actually).
Aside: I was curious about that statement in Intel's manuals, so I devised a test program. Frustratingly, on my computer (2 core i3-4030U), I get this output from it:
unaligned
4265292 / 303932066 | 1.40337%
unaligned, but in same cache line
2373 / 246957659 | 0.000960893%
aligned (8 byte)
0 / 247097496 | 0%
Which seems to violate what Intel says. I will investigate. In the meantime, you should clone that demo program and see what it gives you. You just need -std=c++11 ... -pthread on linux.

Resources