Here's an interesting fact about the GCD: if m >= n, then gcd(m, n) = gcd(m - n, n). We can show this by showing, first, that any common divisor of m and n is also a common divisor of m-n and n, and then that there is no larger common divisor of m-n and n than g (contradiction proof).
The last fact we will need is that gcd(m, n) == gcd(n, m).
Euclid found an algorithm that exploits these facts to calculate the GCD of two numbers M and N, as follows:
#define M 15
#define N 10
void euclid() {
int gcd, tmp, m, n;
m = M;
n = N;
// gcd(m, n) == gcd(M, N)
while (n != 0) {
// gcd(m, n) == gcd(M, N)
if (m >= n)
// gcd(m-n, n) == gcd(M, N)
m = m - n;
else {
// (gcd(m, n) == gcd(M, N)) && (m > n)
tmp = m;
// gcd(tmp, n) == gcd(M, N)
m = n;
// gcd(tmp, m) == gcd(M, N)
n = tmp;
// gcd(n, m) == gcd(M, N)
// gcd(m, n) == gcd(M, N)
}
}
// (gcd(m, n) == gcd(M, N)) && (n == 0)
// gcd(m, 0) == gcd(M, N)
// n = gcd(M, N)
gcd = m;
}
This algorithm has become one of the Grand Old Examples, because it's
simple enough to use as an example, and at the same time complicated
enough to require some thought. How can we convert this to assembly?
First, let's convert to ``simple C.'' We'll do this pretty stupidly
at first, then we'll optimize a little. We'll do the conversion by
applying three rules:
a
or b
, you can only perform a simple
assignment (e.g. res = a;
) - you can't do any
arithmetic and store the result there.
a
or b
, and if it's not just a comparison,
the result must go in a
or b
. What's more,
if the operand is a
then the result must go into
a
, and if the operand is b
then the result
must go into b.
#define M 15
#define N 10
void euclid() {
int gcd, tmp, m, n, a, b;
// m = M;
a = M;
m = a;
// n = N;
b = N;
n = b;
while (n != 0) {
// if (m >= n) {
a = m;
if (a >= n) {
// m = m - n
a = m;
a = a - n;
m = a;
} else {
// tmp = m
a = m;
tmp = a;
// m = n
a = n;
m = a;
// n = tmp
b = tmp;
n = b;
}
}
// gcd = m;
a = m;
gcd = a;
}
To optimize, we'll observe that every time we want to read
m
, a
already has the value we were about to
read in for it. Similarly, b
already has the value for
n
every time we want to read that one. So, we can
eliminate every place we read m
and n
, as
follows:
#define M 15
#define N 10
void euclid() {
int gcd, tmp, a, b;
// m = M;
a = M;
// m = a;
// n = N;
b = N;
// n = b;
while (b != 0) {
// if (m >= n) {
if (a >= b) {
// m = m - n
a = a - b;
} else {
// tmp = m
tmp = a;
// m = n
a = b;
// n = tmp
b = tmp;
}
}
// gcd = m;
gcd = a;
}