Clemson University -- CPSC 231 -- Fall 2009 Data in memory variables have multiple attributes / symbolic name | data type (perhaps with qualifier) | | / allocated in data area, stack, or heap | storage class | duration (lifetime or extent) variable | | scope (visibility of the variable name) | \ linkage (may have been allocated elsewhere) | | address in memory (none if only allocated in register) | alignment | byte order for multibyte variables | | initialization (optional) \ current value simple data types are: char, int, float, double (C uses the keyword void to denote untyped) qualifiers: signed, unsigned, long, short, volatile (compilers sometimes ignore "long", "short") ("volatile" is used to suppress optimizations) data sizes byte - 8 bits = 1 byte - character halfword - 16 bits = 2 bytes - short integer word - 32 bits = 4 bytes - integer, long integer in 32-bit model doubleword - 64 bits = 8 bytes - double, long integer in 64-bit model int main(){ printf("sizeof(char) = %d\n",sizeof(char)); printf("sizeof(short) = %d\n",sizeof(short)); printf("sizeof(int) = %d\n",sizeof(int)); printf("sizeof(long) = %d\n",sizeof(long)); return 0; } compile with just gcc compile with "gcc -m64" sizeof(char) = 1 sizeof(char) = 1 sizeof(short) = 2 sizeof(short) = 2 sizeof(int) = 4 sizeof(int) = 4 sizeof(long) = 4 sizeof(long) = 8 alignment in byte-addressable memory memory access is typically on word-by-word basis (aligned) (original meaning of "memory word" was the unit of transfer to/from memory) alignment restrictions on load/store instructions prevent multiple-byte units from spanning across two memory words, thus allowing individual load or store instructions to make one and only one memory access -- which makes hardware easier to build example: aligned load/store of word at address 0 +--------.--------.--------.--------+ CPU register: |=BYTE=0=|=BYTE=1=|=BYTE=2=|=BYTE=3=| +--------.--------.--------.--------+ ||||||||||||||||||||||||||||||||||| +-----------------------------------------------------------+ |<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>| +-----------------------------------------------------------+ ||||||||||||||||||||||||||||||||||| memory +--------.--------.--------.--------+ word 0: |=BYTE=0=|=BYTE=1=|=BYTE=2=|=BYTE=3=| access this word +--------.--------.--------.--------+ word 4: | byte 4 | byte 5 | byte 6 | byte 7 | +--------.--------.--------.--------+ word 8: | byte 8 | byte 9 | byte a | byte b | +--------.--------.--------.--------+ ... example: unaligned load/store of word at address 2 +--------.--------.--------.--------+ CPU register: |=BYTE=2=|=BYTE=3=|=BYTE=4=|=BYTE=5=| +--------.--------.--------.--------+ \\\\\\\\\\\\\\\\\|///////////////// |||||||| shift network |||||||||| /////////////////|\\\\\\\\\\\\\\\\\ +-----------------------------------------------------------+ |<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>| +-----------------------------------------------------------+ ||||||||||||||||||||||||||||||||||| memory +--------.--------.--------.--------+ word 0: | byte 0 | byte 1 |=BYTE=2=|=BYTE=3=| access two bytes +--------.--------.--------.--------+ word 4: |=BYTE=4=|=BYTE=5=| byte 6 | byte 7 | access two bytes +--------.--------.--------.--------+ word 8: | byte 8 | byte 9 | byte a | byte b | +--------.--------.--------.--------+ ... unaligned access: - may be illegal (SPARC system prints "Bus error (core dumped)") - may cause a trap into the operating system, or, - requires extra shifting network hardware and extra time to perform two memory accesses and the necessary insertion of bytes (i.e., to merge bytes from two memory words into the single-word-length CPU register on a load, or to distribute the bytes from a register into two different memory words on a store) alignment rules: - bytes can start anywhere - halfwords start on a halfword boundary = address divisible by 2 - words start on a word boundary = address divisible by 4 - doublewords start on a doubleword boundary = address divisible by 8 to avoid unaligned accesses the compiler can sometimes reorder or pad data structures you can use the .align pseudo-op in your programs (esp. when you have a word variable allocated after a character string) .asciz "..." (or defensive programming practice will .align 4 always allocate doublewords first, then .word ... words, then halfwords, then strings) however, in some languages (e.g., Fortran, Cobol) unaligned accesses must still be legal for programs, so the operating system on an aligned memory processor must include an unaligned trap handler (or the compiler must generated extra code -- see below) most processors today have added hardware to support unaligned operands byte numbering how are bytes within a multiple byte structure numbered? big-endian => byte 0 is most significant = left-to-right +--------.--------.--------.--------+ word 0: | byte 0 | byte 1 | byte 2 | byte 3 | +--------.--------.--------.--------+ word 4: | byte 4 | byte 5 | byte 6 | byte 7 | +--------.--------.--------.--------+ ... little-endian => byte 0 is least significant = positional representation ... +--------.--------.--------.--------+ | byte 7 | byte 6 | byte 5 | byte 4 | :word 4 +--------.--------.--------.--------+ | byte 3 | byte 2 | byte 1 | byte 0 | :word 0 +--------.--------.--------.--------+ problems arise in sending multibyte data values across networks and in porting software, e.g., if a big-endian computer sends word 0x89abcdef as bytes 0x89, 0xab, ..., then without conversion, a little-endian computer will receive these bytes and assemble them as the received word 0xefcdab89 byte numbering examples 16-bit halfword with value 0xabcd at address 0x100 little-endian big-endian msb lsb msb lsb +----+----+ +----+----+ | ab | cd | | ab | cd | +----+----+ +----+----+ 101 100 100 101 so memory contents for locations 100 and 101 are 100 101 big-endian ab cd little-endian cd ab 32-bits word with value 0x1234 (sign-extended) at address 0x100 little-endian big-endian msb lsb msb lsb +----+----+----+----+ +----+----+----+----+ | 00 | 00 | 12 | 34 | | 00 | 00 | 12 | 34 | +----+----+----+----+ +----+----+----+----+ 103 102 101 100 100 101 102 103 so memory contents for locations 100 through 103 are 100 101 102 103 big-endian 00 00 12 34 little-endian 34 12 00 00 character string "abcdef" in C (null-terminated) +-----+-----+-----+-----+-----+-----+-----+ | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 0 | +-----+-----+-----+-----+-----+-----+-----+ 100 101 102 103 104 105 106 there is no difference in addresses for a character string between big-endian and little-endian 100 101 102 103 104 105 106 big-endian 'a' 'b' 'c' 'd' 'e' 'f' 0 little-endian 'a' 'b' 'c' 'd' 'e' 'f' 0 ---------- example generated code for unaligned access within packed struct #include struct c_and_i{ char x[5]; int y; } __attribute__ (( packed )) test; int main(){ printf("%p %p\n",&(test.x),&(test.y)); test.y = 1; return 0; } ---------- SPARC - uses multiple load/store bytes to accomplish access to unaligned word .file "align.c" .section ".rodata" .align 8 .LLC0: .asciz "%p %p\n" .section ".text" .align 4 .global main .type main, #function .proc 04 main: !#PROLOGUE# 0 save %sp, -112, %sp !#PROLOGUE# 1 sethi %hi(.LLC0), %g1 or %g1, %lo(.LLC0), %o0 sethi %hi(test), %g1 or %g1, %lo(test), %o1 sethi %hi(test+5), %g1 or %g1, %lo(test+5), %o2 call printf, 0 nop sethi %hi(test), %g1 or %g1, %lo(test), %o5 ldub [%o5+5], %g1 // start of unaligned access and %g1, 0, %g1 // stb %g1, [%o5+5] // ldub [%o5+6], %g1 // accesses individual bytes, and %g1, 0, %g1 // so four ldub/stb pairs stb %g1, [%o5+6] // instead of a single st ldub [%o5+7], %g1 // and %g1, 0, %g1 // stb %g1, [%o5+7] // ldub [%o5+8], %g1 // and %g1, 0, %g1 // or %g1, 1, %g1 // stb %g1, [%o5+8] // end of unaligned access mov 0, %g1 mov %g1, %i0 ret restore .size main, .-main .common test,9,1 // 9-byte struct, 1-byte boundary .ident "GCC: (GNU) 3.4.1" ---------- x86 - uses single movl to access unaligned word .file "align.c" .version "01.01" .section .rodata .LC0: .string "%p %p\n" .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp subl $8, %esp andl $-16, %esp movl $0, %eax addl $15, %eax addl $15, %eax shrl $4, %eax sall $4, %eax subl %eax, %esp subl $4, %esp pushl $test+5 pushl $test pushl $.LC0 call printf addl $16, %esp movl $1, test+5 // unaligned access movl $0, %eax leave ret .size main, .-main .comm test,9,1 .ident "GCC: (GNU) 3.4.1" ----------