R1-10

SASIMI 2012 Proceedings

Random Testing of C Compilers Targeting Arithmetic Optimization
Eriko Nagai 1

Hironobu Awazu 2

Nagisa Ishiura 1

Naoya Takeda 3

1

2

School of Science and Technology, Kwansei Gakuin University, Sanda, Hyogo, Japan
Windows Server Business Development Division, Fujitsu Ltd., Kawasaki, Kanagawa, Japan
3
Engineering Headquarters, ITEC Hankyu Hanshin, Co., Ltd, Osaka, Japan

Abstract—This paper presents a method of testing valid-

ity of arithmetic optimization of C compilers using random programs. Compilers are tested by programs whichcontain randomly generated arithmetic expressions. Undeﬁned behavior (such as zero division and signed overﬂow) of the C language is carefully avoided during random program generation. This is based on precise computation of expected values of the expressions which takes
implementation-deﬁned behavior (such as the size of int
and the semantics of shift right on negative integers) into
account. A method for automatic minimization of error
programs is also presented which expedites the analysis
of detected errors. A random test program based on our
method has detected malfunctions in several compilers,
which include LLVM GCC 4.2.1 shipped with the latest
Mac OS X, GCC 4.4.4 for Ubuntu Linux, GCC 4.3.4 for
Cygwin, and GCC 4.4.1 for h8300-elf and m32r-elf.

I.

Introduction

Since compilers are infrastructure tools for developing
various software including operating systems and mission
critical applications, their reliability is one of the criticalissues in system development.
The most common way of validating compilers is theuse of test suites which are sets of programs to check thefunctionalities of the compilers. There are several test
suites for C compilers, such as the one distributed withGCC (GNU Compiler Collection)1 , Plum Hall test suite2 ,AC-TEST3 , SuperTest4 , and testgen25 . By repeated test
suite runs and bug ﬁxes, reliability of compilers are enhanced, to a good extent. However, it is theoretically impossible to validate a compiler completely with a ﬁnite setof test programs, and actually many bugs are reported6
for well-used compilers such as GCC.
Random testing is a complement to test suites, whichattempts to detect compiler malfunctions beyond thereach of the test suites. Compilers are tested by randomly
generated programs as long as time allows. Successful examples are Quest [1], randprog [2], and Csmith [3].
1 http://gcc.gnu.org/install/test.html.

2 http://www.plumhall.com/suites.html.
3 http://www.actest.co.uk/.

4 http://www.ace.nl/compiler/supertest.html.

5 http://ist.ksc.kwansei.ac.jp/˜ishiura/pub/testgen2/.
6 http://gcc.gnu.org/bugzilla/duplicates.cgi.

Although the overall scheme of random testing is simple, there are three subjects to be addressed: How to exclude invalid programs from random generation, how toknow the “correct answers” the randomly generated programs are expected to present, and how to minimize programs which detect errors. Undeﬁned behavior of the C
language makes the ﬁrst issue a challenge. The expectedbehavior of the programs is not always unique, for unspeciﬁed behavior and implementation-deﬁned behavior of C
may make the programs multivocal. Undeﬁned behavior
also hinders automatic minimization of error programs,
which leads to painful hand minimization.
Since Quest [1] focused on passing of arguments andreturn values on function calls, exclusion of undeﬁned behavior and computation of expected values were not diﬃcult. However, targets of random testing is extended to abroader range of program constructs, the three issues become prominent. In Csmith [3], which generates programs
consisting of thousands of lines, proposed variety of techniques to avoid undeﬁned behaviors, but in general it isdiﬃcult to exclude undeﬁned behavior completely. Diﬀerential testing [4], employed in randprog [2] and Csmith,is a workaround for the expected value issue, in whichtest programs are compiled by multiple compilers and theresults are compared. However, it may produce false positives due to unspeciﬁed and implementation-deﬁned behavior of the C language. Since automatic minimizationof suspicious programs is extremely diﬃcult under the existence of undeﬁned behavior, the analysis task to distinguish the false positives and to classify the error programs
takes a lot of time and eﬀorts.
This paper presents random testing of C compilers focusing on arithmetic expressions, whose compilation ismachine dependent and is susceptible to mistakes during
retargeting. A distinctive feature of our method is that byfocusing only on arithmetic expression the expected behavior of randomly generated programs are precisely precomputed taking implementation-deﬁned behavior into
account. As a result, undeﬁned behavior is completely
excluded during program generation and minimization oferror programs becomes possible, which makes the analysis of the error programs eﬃcient.
A random test system based on the proposed method
has detected malfunctions in several compilers which include GCC 4.4.4 for Ubuntu Linux, LLVM GCC 4.2.1 for
Mac OS X, etc.

- 48 -

II.

1: for (speciﬁed iterations or speciﬁed CPU time) {2:
generate a random program p;
3:
if (compile and run(p) == ERROR) {4:
keep p as an error program;
5:
}
6: }
7: analyze (minimize) error programs;

Random Testing of C Compilers

A typical ﬂow of random testing of compilers is shownin Fig. 1. In each iteration, a test program is randomly
generated, which is compiled and executed to check thevalidity of the compilation. This iteration is repeated aslong as time permits. The program generation may focus
on a particular construct of the language (as in Quest [1])or may target broad range of the language speciﬁcation(as in Csmith [3]). If errors are detected, the programsin question are investigated. During this proess, the programs are minimized.
One of the major challenges in this test scheme is how
to randomly generate only valid programs. In the case of
the C language, undeﬁned behavior makes this issue difﬁcult. The undeﬁned behavior is deﬁned as “behavior,such as might arise upon use of an erroneous program
construct or of erroneous data, for which the standard
imposes no requirements.” For example, it is invoked byzero division, signed overﬂow, shift by a negative value,
array reference by an out of range subscript, etc. Since
possible undeﬁned behavior of the C program ranges fromignoring the situation with unpredictable results to terminating execution, programs to test compilers must notinclude constructs that yields undeﬁned behavior. However, it is diﬃcult to detect undeﬁned behavior in randomly generated programs for it depends on the dynamic
behavior of the programs.
How to know the correct behavior for randomly generated programs is another challenge. In general, automaticcomputation of the expected values for random programs
needs enormous eﬀorts, for it is equivalent to implement
an interpreter for (some subset of) the language. Insteadof developing such an interpreter, diﬀerential testing [4]makes use of other compilers. Test programs are compiled
by multiple compilers, or multiple versions of a compiler,
or even a compiler with diﬀerent optimization options.
The execution results are compared, and if they disagree,
the minorities would be suspects.
Although diﬀerential testing enables testing of compilers with large random programs containing many language constructs [3], a large number of false positives
might be produced due to implementation-deﬁned behavior and unspeciﬁed behavior of the C language. Unspeciﬁed and implementation-deﬁned behavior is both “behavior for a correct program construct and correct data, that
depends on the implementation,” where diﬀerence of thetwo types of behavior is whether documentation for such
behavior is required or not. Examples of implementationdeﬁned behavior are size of int variables and the result ofshift right operations on negative integers. Compilers of
diﬀerent target may produce codes of diﬀerent behavior
for the same program. Even if the target is the same, aprogram does not always compiles into the codes of thesame behavior due to unspeciﬁed behavior. For example, there is no restriction on the order of evaluation forsubexpressions in a expression, and optimizers in compilers make use of this freedom to generate more eﬃcientcodes.
Minimization of the error programs is also an importantFig. 1. Flow of Random Testing of Compilers.

1: #include <stdio.h>2:
3: const volatile unsigned char x1 = 2U;
4: const volatile signed long long x6 = 1476669LL;
5: static const unsigned short x8 = 35U;
6:
7: int main (void)
8: {9:
int rc = 0;10:signed long long test = 0;
11:
12:
test = (((x8*(x6<<x8))>=x1)/x6);
13:
14:
if (test == 0LL) {15:
printf("OK, %lld\n",test);
16:
}
17:
else {
18:
rc = 1;19:
printf("NG, %lld\n",test);
20:
}
21:
return rc;
22: }Fig. 2. Example of randomly generated test program. This
program actually found a bug in GCC 4.3.4 for Cygwin.

subject. Firstly, it must be decided if the errors are reallyattributed to compiler malfunctions. This is indispensable
when the test generator produces false positives. Errors
may be due to bugs in the test program generator or misinterpretation of the language speciﬁcation. Unspeciﬁed
behavior makes this decision diﬃcult. Especially the C
language allows a certain degree of freedom in the precision during evaluation of ﬂoating point arithmetic whichmight change the behavior of the programs. Secondly,
the construct in the error program that led to compiler
malfunction should be identiﬁed. Classiﬁcation of the error programs should be also done, because plural error
programs often detect the same type of errors. For those
purposes, each error program must be minimized, namely
reduced to a program which is as small as possible andstill presents the same symptoms.
However, undeﬁned behavior makes automation of theminimization task diﬃcult. While diﬀerential testingmight produce false positives, auto-minimization was not
implemented in Csmith, for unguarded reductions of programs lead to undeﬁned behavior [3].

- 49 -

1: for (;;) {2: randomly generate an expression tree e;
3: for (t=0; t < T ; t++) {4:
randomly assign initial value to each variable;
5:
compute the expected value of e;
6:
if (e does not yield undeﬁned behavior) goto FIN;
7: }
8: }
9: FIN: generate a program from e;
Fig. 3. Generation of arithmetic expressions.

III.

Random Testing of C Compilers Targeting
Arithmetic Optimization

In this paper, random testing of C compilers targetingarithmetic optimization is presented. Our method relies
on precise computation of expected values rather than differential testing. Generation of programs with undeﬁned
behavior is carefully excluded based on computation of expected values. Implementation-deﬁned behavior is faithfully interpreted in the subroutine to compute expectedvalues which is controlled by compiler speciﬁc parameters.

It is possible that the outer loop in Fig. 3 iterates inﬁnitely many times. This happens if e is large and e contains shift operators, whose right operand must be lessthan the width of the left operand. In order to prevent
this situation, the size of e is curved using two parameters pL and pR that deﬁne the probabilities in which eachnode has a left child and a right child, respectively.
The expected values for generated expressions is computed by faithfully taking the implementation-deﬁned behavior of the compiler under test into account. Morespeciﬁcally, integer promotion, arithmetic conversion,
wrap around on unsigned integers, shift right on negative integers as well as detection of undeﬁned behavior
invoked by overﬂow, underﬂow, and shifts with invalid
right operands, are carefully computed based on user deﬁnable target parameters such as the number of bits andthe minimum and maximum values for every integer andﬂoating point types. Target speciﬁc information also includes the function to deﬁne the behavior of shift right
operations on negative integers, and the command nameand the options to invoke the compiler (and a simulator
if it is a cross compiler).
IV.

A.

Test program

Fig. 2 shows an example of the test programs thatare generated by our method. It contains an arithmetic
expression (line 12), which consists of several operators
combining variables. The structure of the expression,
the kinds of the operators, the types/storage classes/type
qualiﬁers of the variables are chosen randomly. Since therandom initial value is declared to every variable (lines
3–5)7 , the expression evaluates to some constant. The
result is compared with the expected value (line 14).

Minimization of Error Programs

Given an error program, such as the one shown in Fig.
2, the arithmetic expression in the program (in line 12) is
reduced into a one that is as small as possible and thatstill let the compiler yield the error. Then, declarationof the variables which do not appear in the minimized
expression are deleted.
A.

Minimization of Arithmetic Expressions

Arithmetic expressions are minimized by applying thefollowing basic transformations repeatedly:

B. Generation of Arithmetic Expressions

1) Substitution of a variable by its initial value

The outline of the algorithm for test program generation is shown in Fig. 3. First, an expression tree e is generated using random numbers (line 2). First, a set of variables are generated. For each variable, its type, sotrage
class (static or none), and modiﬁer (const, volatile,
const volatile, or none), and scope (local or global) are
randomly decided. Then it is decided if the root node ofe is an operation node or a variable node. For an operation node, its operation is randomly chosen from set of all
the arithmetic operations, and its children are generated
recursively. For an variable node, a variable is randomly
selected from the set of variables. Next, random initialvalues are assigned to the variables (line 4). The expectedvalue of the expression is computed (line 5), during which
undeﬁned behavior is detected. If e does not yield undeﬁned behavior (line 6), then a program is generated frome (line 9). On the other hand, if e results in undeﬁned
behavior, new initial values for the variables are generated and e is evaluated again. If e does not pass the test
within T trials, then e is discarded and regenerated.
7 The variables can be either local or global, though this example
has only global variables.

One of the variables in the expression is replaced by
its initial value, as shown in Fig. 4 (a).
2) Evaluation of an operator
One of the leaf subexpressions is replaced by its resulting value, as shown in Fig. 4 (b). Note that achange on the type of constant values such as theone shown in Fig. 4 (c) also takes an application ofthis transformation. This is to identify type related
malfunctions.
3) Choice of top operands
One of the operands of the top operation is chosen
and the other is deleted, as shown in Fig. 4 (d), where
the expected value is also replaced.
In this paper, a minimized form of an error programis deﬁned as a program to which a single application of
any basic transformations results in disappearance of theerror. Note that there are multiple minimized forms for anerror program that satisfy this condition. Our goal is toﬁnd one of them. Depending on compiler bugs, there are- 50 -

1: int x1 = 2; int x2 = 3;
2: int test = ( x1 + x2 ) * x1;
3: if ( test == 10 ) OK else NG

1: unsigned int x3 = 1;
2: unsigned int test = ( -3 + 2 ) * x3;
3: if ( test == 4294967295U ) OK else NG↓↓

1: int x1 = 2; int x2 = 3;
2: int test = ( 2 + x2 ) * x1;
3: if ( test == 10 ) OK else NG

1: unsigned int x3 = 1;
2: unsigned int test = -1 * x3;
3: if ( test == 4294967295U ) OK else NG(a) Basic transformation 1 (substitution of variables)(b) Basic transformation 2 (evaluation of leaf subexpression)

1: unsigned int x3 = 1;
2: unsigned int test = -1 * x3;
3: if ( test == 4294967295U ) OK else NG

1: int x1 = 5; int x2 = 7;
2: int test = ( x1 + x2 ) / x1;
3: if ( test == 2 ) OK else NG↓↓

1: unsigned int x3 = 1;
2: unsigned int test = 4294967295U * x3;
3: if ( test == 4294967295U ) OK else NG

1: int x1 = 5; int x2 = 7;
2: int test = ( x1 + x2 );3: if ( test == 12 ) OK else NG(c) Basic transformation 2 (evaluation of leaf subexpression)(d) Basic transformation 3 (choice of subexpression)

Fig. 4. Basic transformations for minimization.

1: tree topdown minimization(tree r)
2: {3: if (r is a variable) { return r; }4: else {5:
p = generate program(r.left);6:
if (compile and run(p) == ERROR) {7:
return topdown minimization(r.left);8:}9:p = generate program(r.right);
10:
if (compile and run(p) == ERROR) {11:
return topdown minimization(r.right);
12:
}
13:
return r;
14: }
15: }

cases where a single application does not cause error but
multiple application does. Such a kind of minimization isbeyond the scope of this paper.
Reduction of programs using the basic transformations
1 and 2 are referred to as “top-down minimization,” while
reduction using the basic transformation 3 as “bottomup minimization.” Our method attempts to reduce given
error programs by applying the top-down and bottom-up
minimization alternately until they are not applicable any
more. The both minimization procedures runs on the tree
data structure representing arithmetic expressions.
B. Top-Down Minimization
The outline of our algorithm for top-down minimization
is as shown in Fig. 5. It receives a tree r to represent
arithmetic expression and returns a minimized tree. If r
is a leaf (variable) node, it returns r, for there is no room
for top-down minimization (line 3). Otherwise (if the rootof r is an operator), it tests the left subtree (lines 5–8).
It generates a program from the subtree and checks it forcompilation and execution. If error still occurs, it recurses
on the subtree. Otherwise, the same attempt is done on
the right subtree (lines 9–12). Neither subtree yields anerror, it returns r, which means top-down minimization
is not applicable.
C. Bottom-Up Minimization
Bottom-up minimization also receives an expression
tree r and returns a minimized tree. Fig. 6 shows theoutline of the bottom-up minimization algorithm. Set C
keeps candidate nodes which can be replaced by constantnodes while set F keeps the nodes to which substitution
failed (substitution has resulted in disappearance of theerror). The main loop (lines 5–17) continues until there

Fig. 5. Top-down minimization.

is no candidate. On each iteration, an arbitrary node x is
taken out of C, which are evaluated and replaced by the
resulting constant value node. If the resulting program
does not yield the error (lines 9–12), this substitution isa failure; node x is restored and is kept in fail list F . On
the other hand, if the error is detected (lines 13–16), thissubstitution is adopted; C is updated so that it includes
a node which newly becomes evaluable, and the nodes in
F are also included in C, for these nodes may cause theerror on the modiﬁed r. Finally the procedure returns
resulting tree r.
V.

Implementation and Experimental Results

A.

Implementation

A random test program based on the proposed method
has been implemented in Perl 5. It runs on Unix systems,- 51 -TABLE I
Summary of experiments.
compiler (target)
llvm-gcc 4.2.1 (i686-apple-darwin10)
gcc 4.2.1 (i686-apple-darwin10)
gcc 4.3.4 (pc-cygwin)
gcc 4.4.4 (i686-linux)
gcc 4.4.1 (arm-elf)
gcc 4.4.1 (m32r-elf)
gcc 4.4.1 (h8300-elf)

generated
tests
500,000
100,000
10,00050,000200,000
30,00010,000errors
3
3
6
2
0
21
83

error
classes
3
3
6
2
0
5
4

false
positives
0
0
0
19
0
0
17

1: tree bottomup minimization(tree r)
2: {3: C = { all the variable nodes };4: F = φ; /* to keep once failed nodes */5: while( C ) {6:take an arbitrary node x out of C;
7:
evaluate node x and replace x by resulting value;
8:
p = generate program(r);9:if (compile and run(p) != ERROR) {10:
restore x;
11:
F = F ∪ {x};
12:
}
13:
else {
14:
put newly evaluable node into C;
15:
C = C ∪ F ; F = φ;
16:
}
17: }
18: return r;
19: }OS/CPU/memory

time [h]Mac OS X/Core 2 Duo 2.12GHz/2GB
Mac OS X/Core 2 Duo 2.12GHz/2GB
Cygwin/Core 2 Duo 1.40GHz/3.40GB
Ubuntu/Core i5 2.67GHz/4GB
Ubuntu/Core i5 2.67GHz/4GB
Ubuntu/Core i5 2.67GHz/4GB
Ubuntu/Core i5 2.67GHz/4GB

38.5
20.8
5.5
5.1
22.6
2.9
1.1

1: #include <stdio.h>2:
3: const volatile signed long long x6 = 1476669LL;
4:
5: int main (void)
6: {7:
int rc = 0;8:signed long long test = 0;
9:10:
test = (x6<<(signed int)35);
11:
12:
if (test == 50737960496136192LL) {13:
printf("OK, %lld\n",test);
14:
}
15:
else {
16:
rc = 1;
17:
printf("NG, %lld\n",test);
18:
}
19:
return rc;
20: }Fig. 6. Bottom-up minimization.

Fig. 7. Program minimized from error program in Fig. 2.

including Ubuntu Linux, Mac OS X, Cygwin on Windows,
etc. The target standard is C99 (ISO/IEC 9899:1999).
The values of the parameters T , pL , and pR are set to100, 0.45, and 0.45, respectively.

TABLE II
Computation time for minimization.
(GCC 4.4.1 for Cygwin, Core i5 2.67GHz)# operators
before
after
9
1
7
1
7
1
5
2
5
2

B. Error detection capability and run time
TABLE I is a summary of the experiments. The ﬁrst
column lists the versions and targets of the compilers
tested. Column “generated tests” shows the numbers of
random programs generated, “errors” the numbers of programs that detected errors. Some programs detected errors of the same type. Thus, column “error classes” shows
into how many classes they are categorized. The column“false positives” lists the numbers of the programs thatwere reported as errors but turned out to be correct behavior by manual analysis. The last two columns are thecomputation environments and computation time.
With 10 ∼ 40 hours, several malfunctions for each compiler was detected, except for gcc 4.4.1 for arm-elf. All of
the false positives on gcc 4.4.4 for i686-linux and gcc 4.4.1
for h8300-elf were due to the ﬂoating point precision.
C. Minimization
Fig. 7 is the reduced program obtained from the error
program listed in Fig. 2 by our auto-minimizer. The ex-

CPU
[sec]
16.85
6.55
10.53
7.31
9.42

pression in line 12 of Fig. 2, consisting of 5 operations,
was automatically reduced to a single binary operation,from which we can see that malfunction exists in the shift
left operation.
TABLE II lists the CPU time spent for the minimization. The compiler under test was GCC 4.4.1 on Cygwin
and the CPU is Core i5 2.67GHz with 4GB memory. The
computation time is roughly proportional to the numberof the operators in the error programs.
The automatic minimization capability was actually
useful in analyzing the results in TABLE I, especiallythose of gcc 4.4.1 for h8300-elf. Furthermore, the minimizer had been indispensable to ﬁnd bugs in the routine
for computing expected values in earlier versions of our- 52 -

random test system.
D.

Detected Bugs
1: #include <stdio.h>2:
3: unsigned int x = 0;
4:
5: int main (void)
6: {7:
int test = ( -0.5 <= (double) x );8:
if (test != 1) {9:
printf("error (test = %d)\n", test);
10:
}
11:
return 0;
12: }Fig. 8 shows some examples of error programs which
detected compiler bugs by our method. Based on the
outputs of the minimizer, the programs were further minimized by hand. In examples (a) and (d), the compilers
generated codes that yielded erroneous outputs. In examples (b) and (c), the compilers crashed on such shortprograms.
VI.

Conclusion(a) LLVM-GCC 4.2.1 for i686-darwin10 (shipped with Mac OS
10.7 (Lion)) miscompiled this program. With -O1 option, thegenerated code printed "error (test = 0)".

This paper presented a method for random testing of
C compilers targeting optimization of arithmetic expressions. A method of auto-minimizing error programs is
also shown. The test generator based on the methods
detected several bugs in GCCs.
Currently, the ability of the test generator is limited
because the size of the expression is intentionally curved
to avoid undeﬁned behavior. We are now working on thegeneration of longer expressions without undeﬁned behavior. Development of algorithms to accelerate minimization is another important subjects.

1: #include <stdio.h>2:
3: const volatile long long x2 = 15LL;
4:
5: int main (void)
6: {7:
int test = 768 << ( x2 - 1LL );8:
if (test != 12582912) {9:
printf("error (test = %d)\n", test);
10:
}
11:
return 0;
12: }Acknowledgment(b) GCC 4.3.4 for x86-cygwin with -O1 option crashed on thisprogram.

Authors would like to thank Mr. Nobuyuki Hikichi(TOPS Systems Corporation) for his discussion andadvices on compiler testing. We would also like tothank Mr. Shohei Yoshida (now with AXE, Inc.), Mr.
Yuki Uchiyama (now with K-Opticom Corporation), andMr. Soichiro Taga (now with Mitsubishi Electric MicroComputer Application Software Co., Ltd.) for their discussion and help. We thank Mr. Fukumoto, Mr. Ooki,
and all the members of Ishiura Lab. of Kwansei Gakuin
University for their discussion on this research.

1: #include <stdio.h>2:
3: volatile int x = 1;
4:
5: int main (void)
6: {8:long test = 7L * ( 1L >> 1 / x );9:if(test != 0) {10:
printf("error (test = %l\n)", test);
11:
}
12:
return 0;
13: }References(c) GCC 4.4.1 for h8300-elf on Ubuntu Linux with -O1 option
crashed on this program.

[1] Christian Lindig: “Find a Compiler Bug in 5
Minutes,” in Proc. ACM International Symposium
on Automated Analysis-Driven Debugging, pp. 3–12
(Sept. 2005).

1: #include <stdio.h>2:
3: int main (void)
4: {6:volatile short x1 = -1000;
7:long test = 40L * x1;
8:
if (test != -40000L) {9:
printf("error (test = %l)\n", test);
10:
}
11:
return 0;
12: }[2] Eric Eide and John Regehr: “Volatiles Are Miscompiled, and What to Do about It,” in Proc. 7th ACM
International Conference on Embedded Software, pp.
255–264 (Oct. 2008).
[3] Xuejun Yang, Yang Chen, Eric Eide, and John
Regehr: “Finding and Understanding Bugs in C
Compilers,” in Proc. 2011 ACM SIGPLAN Conference on Programing Language Design and Implementation (PLDI) pp. 283–294 (June 2011).
[4] W. M. McKeeman: “Diﬀerential Testing for Software,” Digital Technical Journal, vol. 10, no. 1,
pp. 100–107 (Dec. 1998).

(d) GCC 4.4.1 for M32R miscompiled this program. With -O1
option, the generated code output "error (test = 25536)".
Fig. 8. Example of programs that detected bugs in compilers by
our method.

- 53 -