0

I am implementing a rotate function in AT&T 64 bit assembly and am having trouble getting the correct output in my code.

The function I'm trying to implement is

unsigned long rotate(unsigned long val, ul num, ul dir); 

Val is value I wanted rotated, num is how many times and direction is left or right, 0 being right, 1 being left.

My assembly code:

.global rotate

rotate: #value = %rdi
        #num = %rsi 
        #direction = %rdx 
        mov %rsi, %r10 #puts num into register
        test %rdx, %rdx 
        jz right #if rdx is 0 jump to rotate right
        #else go to loop right below which rotates left
loop: 
     test %r10, %r10 #if num is 0 I am done
     jz done 
     rol $1, %rdi #rotate left 1 bit
     dec %r10 #decrement my count
     jmp loop #jump to top of loop 

 right: #same logic as left 
      test %r10, %10 
      jz done 
      rol $1, %rdi  
      dec %r10 
      jmp loop 
 done: 
      mov %rdi, %rax
      ret 

My C code:

#include <stdio.h> 
extern unsigned long rotate(unsigned long val, unsigned long num, unsigned long direction); 

int main()
{
unsigned long v,n,d; 
v = 0xDEADBEEFDEADBEEF; 
n = 2; 
d = 1; 

printf("%x\n ", rotate(v,n,d));    
}

When I compile and run, I get the value 0x7AB6FBBF, when I am supposed to get 0x7AB6FBBF7AB6FBBF.

Is there something wrong with my instructions not sending in unsigned longs or somethings?

zx485
  • 28,498
  • 28
  • 50
  • 59
pigsploof
  • 35
  • 6
  • Shouldn't it be an unsigned long long then? – Matthieu Brucher Oct 30 '18 at 22:37
  • You know that on most platforms a `long` is 32 bits, right? – Sneftel Oct 30 '18 at 22:38
  • @Sneftel: x86-64 System V has 64-bit `long`, and this code is taking args in RDI and RSI so it's pretty definitely that ABI. ([Where is the x86-64 System V ABI documented?](https://stackoverflow.com/q/18133812)) – Peter Cordes Oct 30 '18 at 22:39
  • I changed the c code to all unsigned long longs and the results are the same – pigsploof Oct 30 '18 at 22:42
  • @pigsploof: x86 has `rol %cl, %rdi` and `ror %cl, %rdi`, which you can use instead of a loop. Simply `mov` your shift count into `%ecx`. – Peter Cordes Oct 30 '18 at 22:43
  • @PeterCordes good point. Though even if the type is wide enough, `%x` is not going to print a full 64 bits. – Sneftel Oct 30 '18 at 22:46
  • @Sneftel: yup, I was still looking at the horribly over-complicated asm and assuming they'd gotten the C correct. You and @zch both spotted the `%x` bug while I was posting an answer about an asm bug :P – Peter Cordes Oct 30 '18 at 22:48
  • Peter's right--all modern processors have barrel shifters that can shift and rotate many bits in a single instruction. No need to loop. – Lee Daniel Crocker Oct 30 '18 at 23:18
  • @LeeDanielCrocker: A barrel shifter makes it possible for the single instruction to have single-cycle latency. Even original 8086 has `ror %cl, %di`, but the imm8 shift/rotate opcodes were added in 286 (http://www.posix.nl/linuxassembly/nasmdochtml/nasmdoca.html). It wasn't until later that these instructions got really fast, but on those old CPUs shifts/rotates cost something like 1 cycle per count. That's faster than any loop could run, especially an inefficient loop with two branches that doesn't look at flags set by `dec`. – Peter Cordes Oct 30 '18 at 23:31

2 Answers2

1

printf("%x", a) works on unsigned int type. On unsigned long you need to use "%lx" as a format string.

zch
  • 14,931
  • 2
  • 41
  • 49
1

You have a bug in your asm: the loop branch in right does jmp loop instead of jmp right. At least that's one of your bugs, IDK if there are more. (update: @zch spotted the bug in your C which explains the truncation you're mentioning.)

This would have been easier to spot if you'd used a better name than loop. e.g. left.

But you shouldn't be looping anyway. x86 has rol %cl, %rdi and ror %cl, %rdi, which you can use instead of a loop. Simply mov your shift count into %ecx, like mov %esi, %ecx.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • the reason I am using the loop is because when I try and use rol %ecx, %rsi I get an error of operand type mismatch for rol. I have to use an immediate for it to compile and I do not know why. – pigsploof Oct 30 '18 at 22:49
  • @pigsploof shift and rotation instructions can only use %cl or immediate as a count operand. – zch Oct 30 '18 at 23:03
  • @pigsploof: my answer says `rol %cl, %rsi` for a reason (http://felixcloutier.com/x86/RCL:RCR:ROL:ROR.html). But `mov %esi, %ecx` can be slightly more efficient than `mov %sil, %cl` or `movzbl %sil, %ecx`, so I recommended that for the `mov`. x86 masks the shift count anyway, so it doesn't matter whether you copy or zero the upper bits of the register, or merge into the old value of RCX. – Peter Cordes Oct 30 '18 at 23:26