1
Modul 12
Data Structure
EL3011 Arsitektur Sistem Komputer
STEI - Institut Teknologi Bandung
2
Contents
1. Arrays
• One-dimensional
• Multi-dimensional (nested)
• Multi-level
2. Structure
• Allocation
• Access
• Alignment
This module adopted from 15-213 Introduction to Computer Systems Lecture, Carnegie Mellon
University, 2020
3
Modul 12. Data Structure
12.2. Arrays
EL3011 Arsitektur Sistem Komputer
STEI - Institut Teknologi Bandung
4
Array Allocation
• Basic Principle
T A[L];
• Array of data type T and length L
• Contiguously allocated region of L * sizeof(T) bytes in
memory
char string[12];
x x + 12
int val[5];
x x+4 x+8 x + 12 x + 16 x + 20
double a[3];
x x+8 x + 16 x + 24
char *p[3];
x x+8 x + 16 x + 24
5
Array Access
• Basic Principle
T A[L];
• Array of data type T and length L
• Identifier A can be used as a pointer to array element 0:
Type T*
int val[5]; 1 5 2 1 3
x x + 4 x + 8 x + 12 x + 16 x + 20
• Reference Type Value
val[4] int
val int *
val+1 int *
&val[2] int *
val[5] int
*(val+1) int
val + i int *
6
Array Access
• Basic Principle
T A[L];
• Array of data type T and length L
• Identifier A can be used as a pointer to array element 0:
Type T*
int val[5]; 1 5 2 1 3
x x + 4 x + 8 x + 12 x + 16 x + 20
• Reference Type Value
val[4] int 3
val int *
val+1 int *
&val[2] int *
val[5] int
*(val+1) int
val + i int *
7
Array Access
• Basic Principle
T A[L];
• Array of data type T and length L
• Identifier A can be used as a pointer to array element 0:
Type T*
int val[5]; 1 5 2 1 3
x x + 4 x + 8 x + 12 x + 16 x + 20
• Reference Type Value
val[4] int 3
val int * x
val+1 int * x + 4
&val[2] int * x + 8
val[5] int ??
*(val+1) int 5 //val[1]
val + i int * x + 4 * i //&val[i]
8
Array Example
#define ZLEN 5
typedef int zip_dig[ZLEN];
zip_dig cmu = { 1, 5, 2, 1, 3 };
zip_dig mit = { 0, 2, 1, 3, 9 };
zip_dig ucb = { 9, 4, 7, 2, 0 };
zip_dig cmu; 1 5 2 1 3
16 20 24 28 32 36
zip_dig mit; 0 2 1 3 9
36 40 44 48 52 56
zip_dig ucb; 9 4 7 2 0
56 60 64 68 72 76
• Declaration “zip_dig cmu” equivalent to “int cmu[5]”
• Example arrays were allocated in successive 20 byte blocks
• Not guaranteed to happen in general
9
Array Accessing Example
zip_dig cmu; 1 5 2 1 3
16 20 24 28 32 36
int get_digit
(zip_dig z, int digit)
{
return z[digit]; n Register %rdi contains
} starting address of array
n Register %rsi contains
x86-64 array index
# %rdi = z n Desired digit at
# %rsi = digit %rdi + 4*%rsi
movl (%rdi,%rsi,4), %eax # z[digit] n Use memory reference
(%rdi,%rsi,4)
10
Array Loop Example
void zincr(zip_dig z) {
size_t i;
for (i = 0; i < ZLEN; i++)
z[i]++;
}
# %rdi = z
movl $0, %eax # i = 0
jmp .L3 # goto middle
.L4: # loop:
addl $1, (%rdi,%rax,4) # z[i]++
addq $1, %rax # i++
.L3: # middle
cmpq $4, %rax # i:4
jbe .L4 # if <=, goto loop
rep; ret
11
Array Loop Example
void zincr(zip_dig z) {
size_t i;
for (i = 0; i < ZLEN; i++)
z[i]++;
}
# %rdi = z
movl $0, %eax # i = 0
jmp .L3 # goto middle
.L4: # loop:
addl $1, (%rdi,%rax,4) # z[i]++
addq $1, %rax # i++
.L3: # middle
cmpq $4, %rax # i:4
jbe .L4 # if <=, goto loop
rep; ret
12
Understanding Pointers & Arrays #1
Decl A1 , A2 *A1 , *A2
Comp Bad Size Comp Bad Size
int A1[3]
int *A2
• Comp: Compiles (Y/N)
• Bad: Possible bad pointer reference (Y/N)
• Size: Value returned by sizeof
13
Understanding Pointers & Arrays #1
Decl A1 , A2 *A1 , *A2
Comp Bad Size Comp Bad Size
int A1[3]
int *A2
A1 Allocated pointer
Unallocated pointer
A2
Allocated int
Unallocated int
• Comp: Compiles (Y/N)
• Bad: Possible bad pointer reference (Y/N)
• Size: Value returned by sizeof
14
Understanding Pointers & Arrays #1
Decl A1 , A2 *A1 , *A2
Comp Bad Size Comp Bad Size
int A1[3] Y N 12 Y N 4
int *A2 Y N 8 Y Y 4
A1 Allocated pointer
Unallocated pointer
A2
Allocated int
Unallocated int
• Comp: Compiles (Y/N)
• Bad: Possible bad pointer reference (Y/N)
• Size: Value returned by sizeof
15
Understanding Pointers & Arrays #2
Decl An *An **An
Cmp Bad Size Cmp Bad Size Cmp Bad Size
int A1[3]
int *A2[3]
int
(*A3)[3]
Allocated pointer
Unallocated pointer
Allocated int
Unallocated int
16
Understanding Pointers & Arrays #2
Decl An *An **An
Cmp Bad Size Cmp Bad Size Cmp Bad Size
int A1[3]
int *A2[3]
int
(*A3)[3]
A1
A2
A3
Allocated pointer
Unallocated pointer
Allocated int
Unallocated int
17
Understanding Pointers & Arrays #2
Decl An *An **An
Cmp Bad Size Cmp Bad Size Cmp Bad Size
int A1[3] Y N 12 Y N 4 N - -
int *A2[3] Y N 24 Y N 8 Y Y 4
int Y N 8 Y Y 12 Y Y 4
(*A3)[3]
A1
A2
A3
Allocated pointer
Unallocated pointer
Allocated int
Unallocated int
18
Multidimensional (Nested) Arrays
• Declaration A[0][0] • • • A[0][C-1]
T A[R][C];
• 2D array of data type T • •
• •
• R rows, C columns • •
• Array Size A[R-1][0] • • • A[R-1][C-1]
• R * C * sizeof(T) bytes
• Arrangement
• Row-Major Ordering
int A[R][C];
A A A A A A
[0] • • • [0] [1] • • • [1] • • • [R-1] • • • [R-1]
[0] [C-1] [0] [C-1] [0] [C-1]
4*R*C Bytes
19
Nested Array Example
#define PCOUNT 4
typedef int zip_dig[5];
zip_dig pgh[PCOUNT] =
{{1, 5, 2, 0, 6},
{1, 5, 2, 1, 3 },
{1, 5, 2, 1, 7 },
{1, 5, 2, 2, 1 }};
zip_dig
1 5 2 0 6 1 5 2 1 3 1 5 2 1 7 1 5 2 2 1
pgh[4];
76 96 116 136 156
• “zip_dig pgh[4]” equivalent to “int
pgh[4][5]”
• Variable pgh: array of 4 elements, allocated contiguously
• Each element is an array of 5 int’s, allocated contiguously
• “Row-Major” ordering of all elements in memory
20
Nested Array Row Access
• Row Vectors
• A[i] is array of C elements of type T
• Starting address A + i * (C * sizeof(T))
int A[R][C];
A[0] A[i] A[R-1]
A A A A A A
[0] ••• [0] • • • [i] ••• [i] • • • [R-1] ••• [R-1]
[0] [C-1] [0] [C-1] [0] [C-1]
A A+(i*C*4) A+((R-1)*C*4)
21
Nested Array Row Access Code
1 5 2 0 6 1 5 2 1 3 1 5 2 1 7 1 5 2 2 1
pgh pgh[2] int *get_pgh_zip(int index)
{
return pgh[index];
}
# %rdi = index
leaq (%rdi,%rdi,4),%rax # 5 * index
leaq pgh(,%rax,4),%rax # pgh + (20 * index)
• Row Vector
• pgh[index] is array of 5 int’s
• Starting address pgh+20*index
• Machine Code
• Computes and returns address
• Compute as pgh + 4*(index+4*index)
22
Nested Array Element Access
• Array Elements
• A[i][j] is element of type T, which requires K bytes
• Address A + i * (C * K) + j * K
= A + (i * C + j) * K
int A[R][C];
A[0] A[i] A[R-1]
A A A A A
[0] ••• [0] • • • ••• [i] ••• • • • [R-1] ••• [R-1]
[0] [C-1] [j] [0] [C-1]
A A+(i*C*4) A+((R-1)*C*4)
A+(i*C*4)+(j*4)
23
Nested Array Element Access Code
1 5 2 0 6 1 5 2 1 3 1 5 2 1 7 1 5 2 2 1
pgh pgh[1][1] int get_pgh_digit(int index, int dig)
{
return pgh[index][dig];
}
leaq (%rdi,%rdi,4), %rax # 5*index
addl %rax, %rsi # 5*index+dig
movl pgh(,%rsi,4), %eax # M[pgh + 4*(5*index+dig)]
• Array Elements
• pgh[index][dig] is int
• Address: pgh + 20*index + 4*dig
= pgh + 4*(5*index + dig)
24
Multi-Level Array Example
zip_dig cmu = { 1, 5, 2, 1, 3 }; • Variable univ denotes array
zip_dig mit = { 0, 2, 1, 3, 9 }; of 3 elements
zip_dig ucb = { 9, 4, 7, 2, 0 }; • Each element is a pointer
#define UCOUNT 3 • 8 bytes
int *univ[UCOUNT] = {mit, cmu, ucb}; • Each pointer points to array
of int’s
cmu
1 5 2 1 3
univ
16 20 24 28 32 36
160 36 mit
0 2 1 3 9
168 16
176 56 ucb 36 40 44 48 52 56
9 4 7 2 0
56 60 64 68 72 76
25
Element Access in Multi-Level Array
int get_univ_digit
(size_t index, size_t digit)
{
return univ[index][digit];
}
salq $2, %rsi # 4*digit
addq univ(,%rdi,8), %rsi # p = univ[index] + 4*digit
movl (%rsi), %eax # return *p
ret
• Computation
• Element access Mem[Mem[univ+8*index]+4*digit]
• Must do two memory reads
• First get pointer to row array
• Then access element within array
26
Array Element Accesses
Nested array Multi-level array
int get_pgh_digit int get_univ_digit
(size_t index, size_t digit) (size_t index, size_t digit)
{ {
return pgh[index][digit]; return univ[index][digit];
} }
Accesses looks similar in C, but address computations very different:
Mem[pgh+20*index+4*digit] Mem[Mem[univ+8*index]+4*digit]
27
N X N Matrix #define N 16
Code typedef int fix_matrix[N][N];
/* Get element A[i][j] */
int fix_ele(fix_matrix A,
• Fixed dimensions size_t i, size_t j)
• Know value of N at {
compile time return A[i][j];
}
#define IDX(n, i, j) ((i)*(n)+(j))
• Variable dimensions, /* Get element A[i][j] */
explicit indexing int vec_ele(size_t n, int *A,
size_t i, size_t j)
• Traditional way to {
implement dynamic return A[IDX(n,i,j)];
arrays }
/* Get element A[i][j] */
int var_ele(size_t n, int A[n][n],
• Variable dimensions, size_t i, size_t j) {
implicit indexing return A[i][j];
• Now supported by gcc }
28
16 X 16 Matrix Access
¢ Array Elements
§ int A[16][16];
§ Address A + i * (C * K) + j * K
§ C = 16, K = 4
/* Get element A[i][j] */
int fix_ele(fix_matrix A, size_t i, size_t j) {
return A[i][j];
}
# A in %rdi, i in %rsi, j in %rdx
salq $6, %rsi # 64*i
addq %rsi, %rdi # A + 64*i
movl (%rdi,%rdx,4), %eax # Mem[A + 64*i + 4*j]
ret
29
n X n Matrix Access
¢ Array Elements
§ size_t n;
§ int A[n][n];
§ Address A + i * (C * K) + j * K
§ C = n, K = 4
§ Must perform integer multiplication
/* Get element A[i][j] */
int var_ele(size_t n, int A[n][n], size_t i, size_t j)
{
return A[i][j];
}
# n in %rdi, A in %rsi, i in %rdx, j in %rcx
imulq %rdx, %rdi # n*i
leaq (%rsi,%rdi,4), %rax # A + 4*n*i
movl (%rax,%rcx,4), %eax # A + 4*n*i + 4*j
ret
30
Example: Array Access
#include <stdio.h>
#define ZLEN 5
#define PCOUNT 4
typedef int zip_dig[ZLEN];
int main(int argc, char** argv) {
zip_dig pgh[PCOUNT] =
{{1, 5, 2, 0, 6},
{1, 5, 2, 1, 3 },
{1, 5, 2, 1, 7 }, linux> ./array
{1, 5, 2, 2, 1 }};
int *linear_zip = (int *) pgh;
int *zip2 = (int *) pgh[2];
int result =
pgh[0][0] +
linear_zip[7] +
*(linear_zip + 8) +
zip2[1];
printf("result: %d\n", result);
return 0;
}
31
Example: Array Access
#include <stdio.h>
#define ZLEN 5
#define PCOUNT 4
typedef int zip_dig[ZLEN];
int main(int argc, char** argv) {
zip_dig pgh[PCOUNT] =
{{1, 5, 2, 0, 6},
{1, 5, 2, 1, 3 },
{1, 5, 2, 1, 7 }, linux> ./array
{1, 5, 2, 2, 1 }};
result: 9
int *linear_zip = (int *) pgh;
int *zip2 = (int *) pgh[2];
int result =
pgh[0][0] +
linear_zip[7] +
*(linear_zip + 8) +
zip2[1];
printf("result: %d\n", result);
return 0;
}
32
Understanding Pointers & Arrays #3
Decl An *An **An
Cmp Bad Size Cmp Bad Size Cmp Bad Size
int A1[3][5]
int *A2[3][5]
int (*A3)[3][5]
int *(A4[3][5])
int (*A5[3])[5]
Decl ***An
• Cmp: Compiles (Y/N)
• Bad: Possible bad pointer Cmp Bad Size
reference (Y/N) int A1[3][5]
• Size: Value returned by int *A2[3][5]
sizeof int (*A3)[3][5]
int *(A4[3][5])
int (*A5[3])[5]
33
Allocated pointer Declaration
Allocated pointer to unallocated int
int A1[3][5]
Unallocated pointer
Allocated int int *A2[3][5]
Unallocated int int (*A3)[3][5]
int *(A4[3][5])
int (*A5[3])[5]
34
Allocated pointer Declaration
Allocated pointer to unallocated int
int A1[3][5]
Unallocated pointer
Allocated int int *A2[3][5]
Unallocated int int (*A3)[3][5]
int *(A4[3][5])
A1 int (*A5[3])[5]
A2/A4
A3
A5
35
Understanding Pointers & Arrays #3
Decl An *An **An
Cmp Bad Size Cmp Bad Size Cmp Bad Size
int A1[3][5] Y N 60 Y N 20 Y N 4
int *A2[3][5] Y N 120 Y N 40 Y N 8
int (*A3)[3][5] Y N 8 Y Y 60 Y Y 20
int *(A4[3][5]) Y N 120 Y N 40 Y N 8
int (*A5[3])[5] Y N 24 Y N 8 Y Y 20
Decl ***An
• Cmp: Compiles (Y/N)
• Bad: Possible bad pointer Cmp Bad Size
reference (Y/N) int A1[3][5] N - -
• Size: Value returned by int *A2[3][5] Y Y 4
sizeof int (*A3)[3][5] Y Y 4
int *(A4[3][5]) Y Y 4
int (*A5[3])[5] Y Y 4
36
Modul 12. Data Structure
12.2. Structures
EL3011 Arsitektur Sistem Komputer
STEI - Institut Teknologi Bandung
37
Structure Representation
r
struct rec {
int a[4];
size_t i; a i next
struct rec *next;
0 16 24 32
};
• Structure represented as block of memory
• Big enough to hold all of the fields
• Fields ordered according to declaration
• Even if another ordering could yield a more compact
representation
• Compiler determines overall size + positions of fields
• Machine-level program has no understanding of the
structures in the source code
38
Generating Pointer to Structure Member
r r+4*idx
struct rec {
int a[4];
size_t i; a i next
struct rec *next;
0 16 24 32
};
• Generating Pointer to int *get_ap
(struct rec *r, size_t idx)
Array Element {
• Offset of each structure return &r->a[idx];
}
member determined at
compile time
# r in %rdi, idx in %rsi
• Compute as r + leaq (%rdi,%rsi,4), %rax
4*idx ret
39
struct rec {
Following Linked List #1 int a[4];
size_t i;
struct rec *next;
• C Code };
r
long length(struct rec*r) {
long len = 0L;
while (r) { a i next
len ++;
r = r->next; 0 16 24 32
}
return len; Register Value
} %rdi r
• Loop assembly code %rax len
.L11: # loop:
addq $1, %rax # len ++
movq 24(%rdi), %rdi # r = Mem[r+24]
testq %rdi, %rdi # Test r
jne .L11 # If != 0, goto loop
40
struct rec {
Following Linked List #2 int a[4];
size_t i;
struct rec *next;
• C Code };
r
void set_val
(struct rec *r, int val)
{ a i next
while (r) {
size_t i = r->i; 0 16 24 32
// No bounds check Element i
r->a[i] = val;
r = r->next; Register Value
} %rdi r
}
%rsi val
.L11: # loop:
movq 16(%rdi), %rax # i = Mem[r+16]
movl %esi, (%rdi,%rax,4) # Mem[r+4*i] = val
movq 24(%rdi), %rdi # r = Mem[r+24]
testq %rdi, %rdi # Test r
jne .L11 # if !=0 goto loop
41
Structures & Alignment
• Unaligned Data struct S1 {
char c;
c i[0] i[1] v
int i[2];
p p+1 p+5 p+9 p+17 double v;
} *p;
• Aligned Data
• Primitive data type requires B bytes implies
Address must be multiple of B
c 3 bytes i[0] i[1] 4 bytes v
p+0 p+4 p+8 p+16 p+24
Multiple of 4 Multiple of 8
Multiple of 8 Multiple of 8
42
Alignment Principles
• Aligned Data
• Primitive data type requires B bytes
• Address must be multiple of B
• Required on some machines; advised on x86-64
• Motivation for Aligning Data
• Memory accessed by (aligned) chunks of 4 or 8 bytes (system
dependent)
• Inefficient to load or store datum that spans cache lines (64 bytes).
Intel states should avoid crossing 16 byte boundaries.
[Cache lines will be discussed in Lecture 11.]
• Virtual memory trickier when datum spans 2 pages (4 KB pages)
[Virtual memory pages will be discussed in Lecture 17.]
• Compiler
• Inserts gaps in structure to ensure correct alignment of fields
43
Specific Cases of Alignment (x86-64)
• 1 byte: char, …
• no restrictions on address
• 2 bytes: short, …
• lowest 1 bit of address must be 02
• 4 bytes: int, float, …
• lowest 2 bits of address must be 002
• 8 bytes: double, long, char *, …
• lowest 3 bits of address must be 0002
44
Satisfying Alignment with Structures
• Within structure: struct S1 {
char c;
• Must satisfy each element’s alignment requirement int i[2];
• Overall structure placement double v;
} *p;
• Each structure has alignment requirement K
• K = Largest alignment of any element
• Initial address & structure length must be multiples of K
• Example:
• K = 8, due to double element
c 3 bytes i[0] i[1] 4 bytes v
p+0 p+4 p+8 p+16 p+24
Multiple of 4 Multiple of 8
Multiple of 8 Multiple of 8
Internal padding
45
Meeting Overall Alignment Requirement
struct S2 {
double v;
• For largest alignment requirement K int i[2];
char c;
• Overall structure must be multiple of K } *p;
External padding
v i[0] i[1] c 7 bytes
p+0 p+8 p+16 p+24
Multiple of K=8
46
Arrays of Structures struct S2 {
• Overall structure length multiple of K double v;
• Satisfy alignment requirement int i[2];
for every element char c;
} a[10];
a[0] a[1] a[2] • • •
a+0 a+24 a+48 a+72
v i[0] i[1] c 7 bytes
a+24 a+32 a+40 a+48
47
Accessing Array Elements
struct S3 {
short i;
float v;
• Compute array offset 12*idx short j;
• sizeof(S3), including alignment spacers } a[10];
• Element j is at offset 8 within structure
• Assembler gives offset a+8
• Resolved during linking
a[0] • • • a[idx] • • •
a+0 a+12 a+12*idx
i 2 bytes v j 2 bytes
a+12*idx a+12*idx+8
short get_j(int idx)
# %rdi = idx
{
leaq (%rdi,%rdi,2),%rax # 3*idx
return a[idx].j;
movzwl a+8(,%rax,4),%eax
}
48
Saving Space
• Put large data types first
struct S4 { struct S5 {
char c; int i;
int i; char c;
char d; char d;
} *p; } *p;
c 3 bytes i d 3 bytes 12 bytes
• Effect (largest alignment requirement K=4)
i c d 2 bytes 8 bytes
49
Example Struct Exam Question
http://www.cs.cmu.edu/~213/oldexams/exam1-f12.pdf
50
Example Struct Exam Question
a X X X X X X X b b b b b b b b
c c c c d d d X e e e e e e e e
f f f f f f f f|
http://www.cs.cmu.edu/~213/oldexams/exam1-f12.pdf
51
Example Struct Exam Question (Cont’d)
http://www.cs.cmu.edu/~213/oldexams/exam1-f12.pdf
52
Example Struct Exam Question (Cont’d)
a d d d c c c c b b b b b b b b
e e e e e e e e f f f f f f f f|
http://www.cs.cmu.edu/~213/oldexams/exam1-f12.pdf
53
Summary
• Arrays
• Elements packed into contiguous region of memory
• Use index arithmetic to locate individual elements
• Structures
• Elements packed into single region of memory
• Access using offsets determined by compiler
• Possible require internal and external padding to ensure alignment
• Combinations
• Can nest structure and array code arbitrarily