See the Immediates section on this 64 bit assembly reference.
Immediate values inside instructions remain 32 bits and their value is sign extended to 64 bits
There just isn't an instruction to move a 64 bit immediate value to memory (*). And because it can be emulated nicely by moving two chunks of 32 bit, there was no reason to introduce new 64 bit instructions. (They could have changed the immediate
movs to always use 64 bits. But considering that most constants you'll ever use are <= 2^31, using 32 bit only saves a lot of space for the upper zero bytes, and costs a bit when you actually use larger constants, so this saves memory).
(*) However, there are instructions to move a 64 bit immediate value to the 64 bit registers, because you can't access the high 32 bit in registers, opposed to memory.
I don't know why your program produced separate addl/adcl instructions; this is what i got from your program:
movq %rsp, %rbp
movl $2041302511, -24(%rbp)
movl $1193046, -20(%rbp)
movl $-2023406815, -16(%rbp)
movl $16632745, -12(%rbp)
movq -16(%rbp), %rax
movq -24(%rbp), %rdx
leaq (%rdx,%rax), %rax
movq %rax, -8(%rbp)
movl $0, %eax
As you can see, the
leaq (%rdx,%rax), %rax adds 64 bit numbers all right. This was a gcc 4.4.7 on a RHEL 6.6 64 bit system. Please, always state your compiler and OS version, as the output may be quite dependent on those.
As long as you're dealing with an x86/amd64 architecture, you can probably rely on local variables being put on the stack, and global variables not on the stack. But please note the concept of 'stack' and 'heap' aren't as clearly defined as it would seem. The
brk/sbrk mechanism of allocating memory is deprecated; modern implementations use
mmap. This might mean you have several small heaps in different sections of your address space. On ARM and MIPS, there's no stack pointer at all - there's just a convention that one specific register serves as the stack pointer, but the instructions to push/pop would work with other registers as well(*). In theory, the compiler is free to do a
mmap() at the start of each function to allocate local memory, and
munmap() it at the end of the function. The only thing the compiler must do is not keep the memory allocated (for reasonable definitions of allocated) after the function exits.
(*) This is a bit of an oversimplification but demonstrates the concept.
Of course, the idea of using
mmap() to make space for local variables is an extreme example, that probably noone uses. But lots of compilers put local variables into processor registers and never reserve space on the stack for them (if you never use a pointer to them, and on architectures that aren't as register-starved as x86). Many architectures use processor registers for function arguments as well. And i've seen microcontroller C compilers that allow you to put all variables local to a function into a static area, if you use a certain keyword so the compiler knows the function isn't called recursively. So, while most of the time, local variables will be placed on the stack, you shouldn't assume this is carved in stone.
The instruction will be misunderstood. The processor can be in 32 bit or 64 bit mode, and the same instructions (in the meaning of: the same sequence of instruction bytes) have different meanings in each of them. For example,
48 89 43 ec is
mov [rbx-20],rax in 64 bit mode, but
dec eax; mov DWORD PTR [ebx-0x14],eax in 32 bit mode.