Is there a case where '&' yields a lower performance than '%'? - c

The following two code sequences produce the same result:
uint mod = val % 4;
uint mod1 = val & 0x3;
I can use both to calculate the same result. I know that in hardware the & operator is much more simpler realised than the % operator. Therefore I expect it to have a better performance than the % operator.
Can I always assume that the & has a better or equal performance?
Does a compiler optimize this automatically?

You can't assume anything about either of these operations, a compile could optimise both to the same instructions.
And, indeed, both clang and gcc will translate them into a single and instruction.
Unfortunately, due to the nature of % having a specified return value for negative values since ISO C99, some extra work is required for signed integers. As opposed to ISO C90, where negative modulo was implementation defined.
The resulting assembly for both operations, on either signed and unsigned values:
modulo with signed integers:
mov eax, DWORD PTR [esp+4] ; grab `val`
cdq ; convert 32-bit EAX to 64-bit
; and fill EDX with the sign bit
shr edx, 30 ; shift EDX by 30 positions to the right
; leaving only the two left-most bits
add eax, edx ; add EDX to EAX
and eax, 3 ; do the AND
sub eax, edx ; subtract EDX from EAX
mov DWORD PTR [esp+8], eax ; move result on stack
This is neat trick, to have properly defined behaviour for negative values. It's doing ((val + 3) & 3) - 3 for negative values, and val & 3 for positive.
and with signed/unsigned and modulo with unsigned:
mov eax, DWORD PTR [esp+4]
and eax, 3
mov DWORD PTR [esp+12], eax

A (modern) compiler should generate the same code from your example, and the reason of this optimization is that the rhs is a compile constant.
As far as I know, the & operator is done with one processor instruction and; it's not the case with the modulo operator which generally implies far more operations (computing the remainder of the integer division). AFAIK, it is generally longer than a multiplication; I consider it as long as an integer division.

Related

How does in assembly does assigning negative number to an unsigned int work?

I Learned About 2's Complement and unsigned and signed int. So I Decided to test my knowledge , as far as i know that a negative number is stored in 2's complement way so that addition and subtraction would not have different algorithm and circuitry would be simple.
Now If I Write
int main()
{
int a = -1 ;
unsigned int b = - 1 ;
printf("%d %u \n %d %u" , a ,a , b, b);
}
Output Comes To Be -1 4294967295 -1 4294967295 . Now , i looked at the bit pattern and various things and then i realized that -1 in 2's complement is 11111111 11111111 11111111 11111111 , so when i interpret it using %d , it gives -1 , but when i interpret using %u , it treat it as a positive number and so it gives 4294967295. I Checked the Assembly Of the code is
.LC0:
.string "%d %u \n %d %u"
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], -1
mov DWORD PTR [rbp-8], -1
mov esi, DWORD PTR [rbp-8]
mov ecx, DWORD PTR [rbp-8]
mov edx, DWORD PTR [rbp-4]
mov eax, DWORD PTR [rbp-4]
mov r8d, esi
mov esi, eax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
leave
ret
Now here -1 is moved to the register both the times in unsigned and signed . What i want to know if reinterpretation is only that matters , then why do we have two types unsigned and signed , it is printf format string %d and %u that matters ?
Further what really happens when i assign negative number to a unsigned integer (I learned That The initializer converts this value from int to unsigned int. ) but in the assembly code I did not saw such a thing. So what really happens ??
And How does Machine knows when it has to do 2's complement and when not , does it see the negative sign and performs 2's complement?
I have read almost every question and answer you could think this question be duplicate of , but I could not find a satisfactory solution.
Both signed and unsigned are pieces of memory and according to operations it matters how they behave.
It doesn't make any difference when adding or subtracting because due to 2-complement the operations are exactly the same.
It matters when we compare two numbers: -1 is lower than 0 while 4294967295 obviously isn't.
About conversion - for the same size it simply takes variable content and moves it to another - so 4294967295 becomes -1. For bigger size it's first signed extended and then content is moves.
How does machine now - according the instruction we use. Machines have either different instructions for comparing signed and unsigned or they provide different flags for it (x86 has Carry for unsigned overflow and Overflow for signed overflow).
Additionally, note that C is relaxed how the signed numbers are stored, they don't have to be 2-complements. But nowadays, all common architectures store the signed like this.
There are a few differences between signed and unsigned types:
The behaviors of the operators <, <=, >, >=, /, %, and >> are all different when dealing with signed and unsigned numbers.
Compilers are not required to behave predictably if any computation on a signed value exceeds the range of its type. Even when using operators which would behave identically with signed and unsigned values in all defined cases, some compilers will behave in "interesting" fashion. For example, a compiler given x+1 > y could replace it with x>=y if x is signed, but not if x is unsigned.
As a more interesting example, on a system where "short" is 16 bits and "int" is 32 bits, a compiler given the function:
unsigned mul(unsigned short x, unsigned short y) { return x*y; }
might assume that no situation could ever arise where the product would exceed 2147483647. For example, if it saw the function invoked as unsigned x = mul(y,65535); and y was an unsigned short, it may omit code elsewhere that would only be relevant if y were greater than 37268.
It seems you seem to have missed the facts that firstly, 0101 = 5 in both signed and unsigned integer values and that secondly, you assigned a negative number to an unsigned int - something your compiler may be smart enough to realise and, therfore, correct to a signed int.
Setting an unsigned int to -5 should technically cause an error because unsigned ints can't store values under 0.
You could understand it better when you try to assign a negative value to a larger sized unsigned integer. Compiler generates the assembly code to do sign extension when transferring small size negative value to larger sized unsigned integer.
see this blog post for assembly level explanation.
Choice of signed integer representation is left to the platform. The representation applies to both negative and non-negative values - for example, if 11012 (-5) is the two's complement of 01012 (5), then 01012 (5) is also the two's complement of 11012 (-5).
The platform may or may not provide separate instructions for operations on signed and unsigned integers. For example, x86 provides different multiplication and division instructions for signed (idiv and imul) and unsigned (div and mul) integers, but uses the same addition (add) and subtraction (sub) instructions for both.
Similarly, x86 provides a single comparison (cmp) instruction for both signed and unsigned integers.
Arithmetic and comparison operations will set one or more status register flags (carry, overflow, zero, etc.). These can be used differently when dealing with words that are supposed to represent signed vs. unsigned values.
As far as printf is concerned, you're absolutely correct that the conversion specifier determines whether the bit pattern 0xFFFF is displayed as -1 or 4294967295, although remember that if the type of the argument does not match up with what the conversion specifier expects, then the behavior is undefined. Using %u to display a negative signed int may or may not give you the expected equivalent unsigned value.

How to get single-operand imul from C code

What do I have to write in C to get the assembler to show the imul with one operand? For example:
imul %ebp
If you want to write C code so that the compiler emits imul with one operand then the only way is to use widening signed multiplication, i.e. cast the operand(s) to a signed type twice the register length. Hence in 32-bit mode the following code will produce the expected output
long long multiply(int x, int y) {
return (long long)x * y;
}
Because non-widening multiplication in 2's complement is the same for both signed and unsigned types, compilers will almost always use multi-operand imul as it's faster and more flexible.
In x86_64 compilers use multi-operand imul to produce 64-bit results even when the inputs were only 32-bits because they still typically have better latency and throughput than single-operand imul. As a result you'll also have to use a double-register-width (128-bit) type like above. Clang, ICC and GCC do support that via __int128, though.
What gcc versions support the __int128 intrinsic type?
Is there a 128 bit integer in gcc?
The below snippet
__int128_t multiply(long long x, long long y) {
return (__int128_t)x * y;
}
will be compiled to
mov rax, rdi
imul rsi
ret
If you use MSVC then you can use _umul128 in 64-bit mode

Unexpected output when executing left-shift by 32 bits

When I do a left shift of a hex I get -1 as output with the following code:
unsigned int i,j=0;
i= (0xffffffff << (32-j));
printf("%d",i);
Similarly when I changed the shift value to 32, the output is 0, but I get warnings from compiler as (left shift count >= width of type)
unsigned int i,j=32;
i= (0xffffffff << (32));
printf("%d",i);
I was expecting the same results in both the cases (ie, 0), but got confused why is displaying wrong output in case #1, and in case #2 the result is correct but the compiler warns!
The result is same in 32 and 64 bit x86 machines.
Can someone explain the results above?
It's undefined behavior to left-shit 32 or greater on a 32-bit integer. That's what the error is about.
C11 6.5.7 Bitwise shift operators
The integer promotions are performed on each of the operands. The type of the result is
that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
Shifting a 32-bit variable by 32 yields undefined behavior.
Here is the assembly generated by the VS-2013 compiler:
int n = 0;
mov dword ptr [n],0
int a = 0xFFFFFFFF << 32;
mov dword ptr [a],0
int b = 0xFFFFFFFF << (32-n);
mov ecx,20h
sub ecx,dword ptr [n]
or eax,0FFFFFFFFh
shl eax,cl
mov dword ptr [b],eax
As you can see, what happens de-facto is:
When you shift by a constant value of 32, the compiler simply sets the result to 0
When you shift by a variable (such as 32-n with n==0), the compiler uses shl
The actual result of shl depends on the implementation of this operation in the underlying architecture. On your processor, it probably takes the 2nd operand modulo 32, hence the 1st operand is shifted by 0.
Again, the description above is not dictated by the standard, so it really depends on the compiler in use.

test if hexadecimal number is in range of 16 bits register

addInt:
clc
mov ax, cx
add ax, bx
JNC convert
how would i be able to test if the sum is in range of 16 bits, since if I add using 16 bits register the result does not show the carry over value even if the sum is greater than 16 bits, OF will not work either since it will never become overflow due to the use of 16 bits registers. How should i continue this code to make it jump to convert loop. for example if I have FFFF + FFFE, the sum will be 1FFFD, but the eax register will only show FFFD, without the carry over 1
thank in advance for helping
You should be able to tell after the add instruction if your resultant value is larger than 16-bits.
The ADD instruction performs integer addition. It evaluates the result for both signed
and unsigned integer operands and sets the OF and CF flags to indicate a carry (overflow) in the signed or unsigned result, respectively. The SF flag indicates the sign of
the signed result.
Since you appear to be dealing with unsigned 16-bit values, you should look at CF, the carry flag after the addition:
addInt:
clc
mov ax, cx
add ax, bx ; Sets CF if result is larger than 16-bits
jc .larger_than_16_bits

Multiplication In MASM611

hii i have a problem in multiplication for above 5 bits i'm working in MASM611 plz tell me the solution which is for atleast 8 bit multiplication in MASM611
for unsigned integer multiply, use mul; for signed use imul. if the operand is an 8-bit register, you get a 16-bit result.
for example:
mov al, 3
mov cl, 5
mul cl
puts 15 in ax.
study this page: http://web.archive.org/web/20120414214754/http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/Chapter_6/CH06-2.html

Resources