Subject: long long problem

From: "Hanske;Stefan" <[email-masked]>
Date: Thu, 18 May 2000 02:49:07 +0200 (METDST)

Hi all,

I think that the following behaviour of gcc is intended, but it's strange
that gcc uses only one register to return a long long value from a
function and expands it in a curious way to be a 64-bit value again
(something like mov r1, r0, asr #31).

The following just won't work:

	long long foo(long long test) {return test;}

	int main() {
		long long test = 0x1010101010101010;
		if (foo(test) == test) printf("I'm happy");
	}

Another thing that just won't work is the use of llseek() in defrag. I had
to use the syscall wrapper and change all the function calls to its own 
llseek() implementation to use long long * as the return value. Thats what
the kernel does. But this means that the libc llseek() won't work in every
case (when the returned value is compared to a "real" long long the
values will not match even if they should).

Does APCS definitely forbid the use of r0 & r1 as return registers for
long longs??

Thanks for any answers

unsubscribe: body of `unsubscribe linux-arm' to [email-masked]
++        Please use [email-masked] for           ++
++                        kernel-related discussions.                      ++

---

From: Philip Blundell <[email-masked]>
Date: Thu, 18 May 2000 08:23:29 +0100

In message <[email-masked]>, 
>		if (foo(test) == test) printf("I'm happy");

Pardon?  It works for me.  Here's the generated assembler.

@ Generated by gcc 2.96 19991025 (experimental) for ARM/elf
	.file	"longlong.c"
gcc2_compiled.:
.text
	.align	2
	.global	foo
	.type	 foo,function
foo:
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, current_function_anonymous_args = 0
	mov	pc, lr
.Lfe1:
	.size	 foo,.Lfe1-foo
.section	.rodata
	.align	2
.LC0:
	.ascii	"I'm happy\000"
.text
	.align	2
	.global	main
	.type	 main,function
main:
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, current_function_anonymous_args = 0
	stmfd	sp!, {r4, r5, lr}
	adr	r4, .L5
	ldmia	r4, {r4-r5}
	mov	r1, r5
	mov	r0, r4
	bl	foo
	cmp	r0, r4
	ldmnefd	sp!, {r4, r5, pc}
	cmp	r1, r5
	ldr	r0, .L5+8
	ldmnefd	sp!, {r4, r5, pc}
	ldmfd	sp!, {r4, r5, lr}
	b	printf
.L6:
	.align	2
.L5:
	.word	269488144
	.word	269488144
	.word	.LC0
.Lfe2:
	.size	 main,.Lfe2-main
	.ident	"GCC: (GNU) 2.96 19991025 (experimental)"

>Another thing that just won't work is the use of llseek() in defrag.

This should be fine as well.  Are you sure you had a prototype in scope?

p.

---

From: Russell King - ARM Linux Admin <[email-masked]>
Date: Thu, 18 May 2000 08:17:16 +0100 (BST)

Hanske;Stefan writes:
> <code removed>

Well, I've just tested it on the old gcc 2.7.2.2 and gcc 2.95.1, and it
works correctly with both of thoes compilers.  I would suggest that you've
found a compiler bug!
   _____
  |_____| ------------------------------------------------- ---+---+-
  |   |        Russell King       [email-masked]      --- ---
  | | | |            http://www.arm.linux.org.uk/            /  /  |
  | +-+-+                                                     --- -+-
  /   |               THE developer of ARM Linux              |+| /|\
 /  | | |                                                     ---  |
    +-+-+ -------------------------------------------------  /\\\  |

---

From: "Hanske;Stefan" <[email-masked]>
Date: Fri, 19 May 2000 04:19:12 +0200 (METDST)

> Pardon?  It works for me.  Here's the generated assembler.

Here is what I get for a small llseek() test program using libc with
_FILE_OFFSET_BITS=64:

#include <sys/types.h>
#include <stdio.h>

int main() {
        FILE * file;
        loff_t offset;

        file = fopen("/dev/zero","r");
        offset = llseek(file, 0x1000, SEEK_SET);
        printf("The offset is: %lld.\n", offset);
}

=====>

0x20003bc <main>:       mov     r12, sp
0x20003c0 <main+4>:     stmdb   sp!, {r11, r12, lr, pc}
0x20003c4 <main+8>:     ldr     r0, [pc, #28]   ; 0x20003f4 <main+56>
0x20003c8 <main+12>:    ldr     r1, [pc, #28]   ; 0x20003f8 <main+60>
0x20003cc <main+16>:    sub     r11, r12, #4    ; 0x4
0x20003d0 <main+20>:    bl      0x20002f8 <fopen>
0x20003d4 <main+24>:    mov     r1, #4096       ; 0x1000
0x20003d8 <main+28>:    mov     r2, #0  ; 0x0
0x20003dc <main+32>:    bl      0x20002c8 <llseek>	<====
0x20003e0 <main+36>:    mov     r1, r0			<====
0x20003e4 <main+40>:    mov     r2, r1, asr #31		<====
0x20003e8 <main+44>:    ldr     r0, [pc, #c]    ; 0x20003fc <main+64>
0x20003ec <main+48>:    bl      0x20002e8 <printf>
0x20003f0 <main+52>:    ldmdb   r11, {r11, sp, pc}
0x20003f4 <main+56>:    andeq   r0, r0, #1879048192     ; 0x70000000
0x20003f8 <main+60>:    andeq   r0, r0, #2080374784     ; 0x7c000000
0x20003fc <main+64>:    andeq   r0, r0, #-2147483648    ; 0x80000000
End of assembler dump.

This is using gcc version 2.95.3 19991030 (prerelease)

---

From: Philip Blundell <[email-masked]>
Date: Fri, 19 May 2000 10:21:49 +0100

In message <[email-masked]>, "
>        offset = llseek(file, 0x1000, SEEK_SET);

You need lseek (which is actually lseek64 when _FILE_OFFSET_BITS=64), not 
llseek.  The latter has no prototype; if you really must use it you will need 
to provide your own declaration.

p.

---

From: Russell King - ARM Linux Admin <[email-masked]>
Date: Fri, 19 May 2000 09:42:06 +0100 (BST)

> This is using gcc version 2.95.3 19991030 (prerelease)

Try compiling it with -Wall.  You will get warnings.  For starters,
llseek doesn't take a FILE * argument as the first parameter.  Secondly,
it returns an "int".

Try:
	file = fopen("/dev/zero", "r");
	fseeko64(file, 0x1000ULL, SEEK_SET);
	offset = ftello64(file);
	printf("The offset is %lld.\n", offset);

That appears to work as expected.  (Note that fseeko64 does not appear to
return the new file position, and, in addition you need to define
_LARGEFILE64_SOURCE).
   _____
  |_____| ------------------------------------------------- ---+---+-
  |   |        Russell King       [email-masked]      --- ---
  | | | |            http://www.arm.linux.org.uk/            /  /  |
  | +-+-+                                                     --- -+-
  /   |               THE developer of ARM Linux              |+| /|\
 /  | | |                                                     ---  |
    +-+-+ -------------------------------------------------  /\\\  |

---

From: Paul Koning <[email-masked]>
Date: Fri, 19 May 2000 09:23:03 -0400 (EDT)

>>>>> "Russell" == Russell King <- ARM Linux Admin <[email-masked]>> writes:

 Russell> Hanske;Stefan writes:
 >> Here is what I get for a small llseek() test program using libc
 >> with _FILE_OFFSET_BITS=64: file = fopen("/dev/zero","r"); offset =
 >> llseek(file, 0x1000, SEEK_SET); printf("The offset is: %lld.\n",
 >> offset);
 >> 

 Russell> Try compiling it with -Wall.  You will get warnings.  For
 Russell> starters, llseek doesn't take a FILE * argument as the first
 Russell> parameter.  Secondly, it returns an "int".

I guess it's a naming error then -- because surely naming something
"llseek" when it doesn't return a "long long int" is a misleading
thing to do...

      paul

---

From: Philip Blundell <[email-masked]>
Date: Fri, 19 May 2000 17:20:44 +0100

>"llseek" when it doesn't return a "long long int" is a misleading

I think Russell is mistaken; the return value of llseek() is actually loff_t, 
which is equivalent to long long.

p.

---

From: Russell King - ARM Linux Admin <[email-masked]>
Date: Fri, 19 May 2000 18:41:08 +0100 (BST)

> which is equivalent to long long.

Probably - just checked my only glibc source (2.0.99) and it does return
loff_t.  However, if its not prototyped, then GCC will default it to return
an int.

Therefore, there are still two errors on the program:

1. It's passing a FILE * instead of an int as the first argument.
2. The C compiler believes, since there is no prototype to tell it otherwise,
   that llseek returns an int.

1 can be fixed by using fseeko64 - there is no fseek64 in that version of glibc,
nor the later glibc headers.

2 can be fixed merely by supplying the appropriate prototype:

	loff_t __llseek (int fd, loff_t offset, int whence);
   _____

---

From: "Hanske;Stefan" <[email-masked]>
Date: Fri, 19 May 2000 23:50:51 +0200 (METDST)

> "llseek" when it doesn't return a "long long int" is a misleading
Might be right. But in glibc, llseek() is a week reference to lseek64(),
the compiler didn't complain about implicit declaration of function
llseek() when stdio.h was included, so I thought it would have a
prototype. Btw, when disassembling lseek64() in glibc, I found that
something similar is made to the return value there. Don't know what to do
about that. On i386-platforms it is supposed to work with glibc-2.* .

If the above solutions are ment to be portable, I'll see if I can convince
some responsible person to change the  source code.

Thank you very much.