1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# !> Three versions of factorial in assembly

.global main

.text

# Assembly Design Recipe

# 1. Signature / purpose

# long fact(long n)
# Computes the nth factorial

# 2. Pseudocode

# long fact(long n) {
#   if (n < 2) 
#     return 1;
#   else
#     temp = fact(n - 1);
#     return n * temp;
# }

# 3. Variable mappings 
# n -> %rdi -> %r12
# temp -> %rax

# 4. Skeleton

# 5. Complete the body

# Version 1: callee-save register

# long fact(long n)

fact:
  # PROLOGUE
  pushq %r12     # push 8 bytes
  enter $8, $0   # allocate 8 bytes to align the stack pointer

  # BODY
  #   if (n < 2) 
  cmpq $2, %rdi
  jge fact_else
  #     return 1;
  movq $1, %rax
  jmp fact_ret
fact_else:
  #   else {
  movq %rdi, %r12
  subq $1, %rdi
  call fact
  #     temp = fact(n - 1);
  #     return n * temp;
  imulq %r12, %rax

  # EPILOGUE
fact_ret:
  leave
  popq %r12
  ret
  #   }

# Version 2: Using local variables in the stack frame

# n -> %rdi -> -8(%rbp)
# temp -> %rax
fact2:
  # PROLOGUE
  enter $16, $0   # allocate 8 bytes + 8 bytes to align rsp

  # BODY
  #   if (n < 2) 
  cmpq $2, %rdi
  jge fact2_else
  #     return 1;
  movq $1, %rax
  jmp fact2_ret
fact2_else:
  #   else {
  movq %rdi, -8(%rbp)
  subq $1, %rdi
  call fact2
  #     temp = fact2(n - 1);
  #     return n * temp;
  imulq -8(%rbp), %rax

  # EPILOGUE
fact2_ret:
  leave
  ret
  #   }

# Version 3: Using a caller-save register

# n -> %r10
fact3:
  # PROLOGUE
  enter $0, $0

  # BODY
  #   if (n < 2) 
  cmpq $2, %rdi
  jge fact3_else
  #     return 1;
  movq $1, %rax
  jmp fact3_ret
fact3_else:
  #   else {
  movq %rdi, %r10
  subq $1, %rdi
  pushq %r10    # push twice to align the stack pointer
  pushq %r10
  call fact3
  #     temp = fact3(n - 1);
  #     return n * temp;
  popq %r10
  popq %r10
  imulq %r10, %rax

  # EPILOGUE
fact3_ret:
  leave
  ret
  #   }


# int main(int argc, char *argv[])
# tmp -> %rax
main:
  enter $0, $0

  # tmp = fact(5)
  movq $5, %rdi
  call fact3

  # printf("%ld\n", tmp)
  movq $format, %rdi
  movq %rax, %rsi
  movb $0, %al
  call printf

  # return 0;
  movq $0, %rax
  leave
  ret

.data
format:
  .asciz "%ld\n"