I look forward to your dhrystone2.2 performance reply.
What was it? 3000 dhrystones/second with the optimizer?
Your C plane needs some hangar time.
Yes, and I'll take your benchmark results seriously when you can compile the original benchmark sources, using full versions of the C libraries.
You know, the silly thing is that you didn't really need to resort to trickery to get good benchmark results - I'd be happy to acknowledge that GCC has a better optimizer than Catalina, and can produce faster code. On the other hand, Catalina produces smaller code than GCC, and so can fit the original benchmarks comfortably in Hub RAM without requiring them to be modified.
Yes, and I'll take your benchmark results seriously when you can compile the original benchmark sources, using full versions of the C libraries.
You know, the silly thing is that you didn't really need to resort to trickery to get good benchmark results - I'd be happy to acknowledge that GCC has a better optimizer than Catalina, and can produce faster code. On the other hand, Catalina produces smaller code than GCC, and so can fit the original benchmarks comfortably in Hub RAM without requiring them to be modified.
Ross.
There are no modifications. Show me where they are different.
Which "full C libraries" do you want to use for comparison?
Our libraries are designed to fit the needs of my primary customer.
There is more than one way to achieve the goal.
$ propeller-load -r -t *.elf -p 28
Propeller Version 1 on COM28
Writing 27308 bytes to Propeller RAM.
Verifying ... Upload OK!
Dhrystone Benchmark, Version C, Version 2.2
Program compiled without 'register' attribute
Using STDC clock(), HZ=80000000
Trying 5000 runs through Dhrystone:
Measured time too small to obtain meaningful results
Trying 50000 runs through Dhrystone:
Final values of the variables used in the benchmark:
Int_Glob: 5
should be: 5
Bool_Glob: 1
should be: 1
Ch_1_Glob: A
should be: A
Ch_2_Glob: B
should be: B
Arr_1_Glob[8]: 7
should be: 7
Arr_2_Glob[8][7]: 55010
should be: Number_Of_Runs + 10
Ptr_Glob->
Ptr_Comp: 27312
should be: (implementation-dependent)
Discr: 0
should be: 0
Enum_Comp: 2
should be: 2
Int_Comp: 17
should be: 17
Str_Comp: DHRYSTONE PROGRAM, SOME STRING
should be: DHRYSTONE PROGRAM, SOME STRING
Next_Ptr_Glob->
Ptr_Comp: 27312
should be: (implementation-dependent), same as above
Discr: 0
should be: 0
Enum_Comp: 1
should be: 1
Int_Comp: 18
should be: 18
Str_Comp: DHRYSTONE PROGRAM, SOME STRING
should be: DHRYSTONE PROGRAM, SOME STRING
Int_1_Loc: 5
should be: 5
Int_2_Loc: 13
should be: 13
Int_3_Loc: 7
should be: 7
Enum_Loc: 1
should be: 1
Str_1_Loc: DHRYSTONE PROGRAM, 1'ST STRING
should be: DHRYSTONE PROGRAM, 1'ST STRING
Str_2_Loc: DHRYSTONE PROGRAM, 2'ND STRING
should be: DHRYSTONE PROGRAM, 2'ND STRING
Microseconds for one run through Dhrystone: 143
Dhrystones per Second: 6954
#ifdef NOTDEFINED /* To compile and run this file, say "sh dry.c" */
case $0 in
*.c) ;;
sh) echo 'Use "sh dry.c", not "sh < dry.c"' >&2; exit 1;;
*) echo 'Filename must end in ".c"' >&2; exit 1;;
esac
echo "${CC=cc} -c ${CFLAGS} $0 -o dry1.o"
${CC} -c ${CFLAGS} $0 -o dry1.o || exit 1
echo "${CC} -DPASS2 ${CFLAGS} $0 dry1.o ${LFLAGS} -o dry2"
${CC} -DPASS2 ${CFLAGS} $0 dry1.o ${LFLAGS} -o dry2 || exit 1
./dry2 ${1-50000} 2>/dev/null
echo "${CC=cc} -c -DREG ${CFLAGS} $0 -o dry1.o"
${CC} -c -DREG ${CFLAGS} $0 -o dry1.o || exit 1
echo "${CC} -DPASS2 -DREG ${CFLAGS} $0 dry1.o ${LFLAGS} -o dry2nr"
${CC} -DPASS2 -DREG ${CFLAGS} $0 dry1.o ${LFLAGS} -o dry2nr || exit 1
./dry2nr ${1-50000} 2>/dev/null
echo "${CC=cc} -c -O ${CFLAGS} $0 -o dry1.o"
${CC} -c -O ${CFLAGS} $0 -o dry1.o || exit 1
echo "${CC} -DPASS2 -O ${CFLAGS} $0 dry1.o ${LFLAGS} -o dry2o"
${CC} -DPASS2 -O ${CFLAGS} $0 dry1.o ${LFLAGS} -o dry2o || exit 1
./dry2o ${1-50000} 2>/dev/null
rm -f dry1.o
exit 0
#endif
/****************** "DHRYSTONE" Benchmark Program ***************************/
#define Version "C, Version 2.2"
/* File: dhry_1.c (part 2 of 3)
* Author: Reinhold P. Weicker
* Siemens Nixdorf, Paderborn/Germany
* weicker@specbench.org
* Date: May 25, 1988
* Modified: Steven Pemberton, CWI, Amsterdam; Steven.Pemberton@cwi.nl
* Date: October, 1993; March 1995
* Included both files into one source, that gets compiled
* in two passes. Made program auto-compiling, and auto-running,
* and generally made it much easier to use.
*
* Original Version (in Ada) published in
* "Communications of the ACM" vol. 27., no. 10 (Oct. 1984),
* pp. 1013 - 1030, together with the statistics
* on which the distribution of statements etc. is based.
*
* In this C version, the following C library functions are used:
* - strcpy, strcmp (inside the measurement loop)
* - printf, scanf (outside the measurement loop)
* In addition, Berkeley UNIX system calls "times ()" or "time ()"
* are used for execution time measurement. For measurements
* on other systems, these calls have to be changed.
*
* Collection of Results:
* Reinhold Weicker (address see above) and
*
* Rick Richardson
* PC Research. Inc.
* 94 Apple Orchard Drive
* Tinton Falls, NJ 07724
* Phone: (201) 389-8963 (9-17 EST)
* Usenet: ...!uunet!pcrat!rick
*
* Please send results to Rick Richardson and/or Reinhold Weicker.
* Complete information should be given on hardware and software used.
* Hardware information includes: Machine type, CPU, type and size
* of caches; for microprocessors: clock frequency, memory speed
* (number of wait states).
* Software information includes: Compiler (and runtime library)
* manufacturer and version, compilation switches, OS version.
* The Operating System version may give an indication about the compiler;
* Dhrystone itself performs no OS calls in the measurement loop.
*
* The complete output generated by the program should be mailed
* such that at least some checks for correctness can be made.
*
***************************************************************************
*
* Defines: The following "Defines" are possible:
* -DREG (default: Not defined)
* As an approximation to what an average C programmer
* might do, causes the "register" storage class to be applied
* - for local variables, if they are used (dynamically)
* five or more times
* - for parameters if they are used (dynamically)
* six or more times
* Note that an optimal "register" strategy is
* compiler-dependent, and that "register" declarations
* do not necessarily lead to faster execution.
* -DNOSTRUCTASSIGN (default: Not defined)
* Define if the C compiler does not support
* assignment of structures.
* -DNOENUMS (default: Not defined)
* Define if the C compiler does not support
* enumeration types.
* -DTIMES (default)
* -DTIME
* The "times" function of UNIX (returning process times)
* or the "time" function (returning wallclock time)
* is used for measurement.
* For single user machines, "time ()" is adequate. For
* multi-user machines where you cannot get single-user
* access, use the "times ()" function. If you have
* neither, use a stopwatch in the dead of night.
* "printf"s are provided marking the points "Start Timer"
* and "Stop Timer". DO NOT use the UNIX "time(1)"
* command, as this will measure the total time to
* run this program, which will (erroneously) include
* the time to allocate storage (malloc) and to perform
* the initialization.
* -DHZ=nnn
* In Berkeley UNIX, the function "times" returns process
* time in 1/HZ seconds, with HZ = 60 for most systems.
* CHECK YOUR SYSTEM DESCRIPTION BEFORE YOU JUST APPLY
* A VALUE.
*
***************************************************************************
*
* History: Version C/2.1 was made for two reasons:
*
* 1) There was an obvious need for a common C version of
* Dhrystone, since C is at present the most popular system
* programming language for the class of processors
* (microcomputers, minicomputers) where Dhrystone is used most.
* There should be, as far as possible, only one C version of
* Dhrystone such that results can be compared without
* restrictions. In the past, the C versions distributed
* by Rick Richardson (Version 1.1) and by Reinhold Weicker
* had small (though not significant) differences.
*
* 2) As far as it is possible without changes to the Dhrystone
* statistics, optimizing compilers should be prevented from
* removing significant statements.
*
* This C version has been developed in cooperation with
* Rick Richardson (Tinton Falls, NJ), it incorporates many
* ideas from the "Version 1.1" distributed previously by
* him over the UNIX network Usenet.
* I also thank Chaim Benedelac (National Semiconductor),
* David Ditzel (SUN), Earl Killian and John Mashey (MIPS),
* Alan Smith and Rafael Saavedra-Barrera (UC at Berkeley)
* for their help with comments on earlier versions of the
* benchmark.
*
* Changes: In the initialization part, this version follows mostly
* Rick Richardson's version distributed via Usenet, not the
* version distributed earlier via floppy disk by Reinhold Weicker.
* As a concession to older compilers, names have been made
* unique within the first 8 characters.
* Inside the measurement loop, this version follows the
* version previously distributed by Reinhold Weicker.
*
* At several places in the benchmark, code has been added,
* but within the measurement loop only in branches that
* are not executed. The intention is that optimizing compilers
* should be prevented from moving code out of the measurement
* loop, or from removing code altogether. Since the statements
* that are executed within the measurement loop have NOT been
* changed, the numbers defining the "Dhrystone distribution"
* (distribution of statements, operand types and locality)
* still hold. Except for sophisticated optimizing compilers,
* execution times for this version should be the same as
* for previous versions.
*
* Since it has proven difficult to subtract the time for the
* measurement loop overhead in a correct way, the loop check
* has been made a part of the benchmark. This does have
* an impact - though a very minor one - on the distribution
* statistics which have been updated for this version.
*
* All changes within the measurement loop are described
* and discussed in the companion paper "Rationale for
* Dhrystone version 2".
*
* Because of the self-imposed limitation that the order and
* distribution of the executed statements should not be
* changed, there are still cases where optimizing compilers
* may not generate code for some statements. To a certain
* degree, this is unavoidable for small synthetic benchmarks.
* Users of the benchmark are advised to check code listings
* whether code is generated for all statements of Dhrystone.
*
* Version 2.1 is identical to version 2.0 distributed via
* the UNIX network Usenet in March 1988 except that it corrects
* some minor deficiencies that were found by users of version 2.0.
* The only change within the measurement loop is that a
* non-executed "else" part was added to the "if" statement in
* Func_3, and a non-executed "else" part removed from Proc_3.
*
* Version C/2.2, Steven Pemberton, October 1993
* Functionally, identical to version 2.2; the changes are in
* how you compile and use it:
* - Everything is in one file now, but compiled in 2 passes
* - Compile (and run) by running the file through the shell: 'sh dhry.c"
* - Uses the system definition of HZ if one can be found
* - HZ must be defined, otherwise it won't compile (no defaults here)
* - The (uninteresting) output is printed to stderr (dhry2 > /dev/null)
* - The number of loops is passed as a parameter, rather than read
* (dhry2 500000)
* - If the number of loops is insufficient to get a good result,
* it repeats it with loops*10 until it is enough (rather than just
* stopping)
* - Output says which sort of clock it is using, and the HZ value
* - You can use -DREG instead of the -DREG=register of previous versions
* - Some stylistic cleanups.
*
***************************************************************************
*
* Compilation model and measurement (IMPORTANT):
*
* The following "ground rules" apply for measurements:
* - Separate compilation
* - No procedure merging
* - Otherwise, compiler optimizations are allowed but should be indicated
* - Default results are those without register declarations
* See the companion paper "Rationale for Dhrystone Version 2" for a more
* detailed discussion of these ground rules.
*
* For 16-Bit processors (e.g. 80186, 80286), times for all compilation
* models ("small", "medium", "large" etc.) should be given if possible,
* together with a definition of these models for the compiler system used.
*
**************************************************************************
*
* Dhrystone (C version) statistics:
*
* [Comment from the first distribution, updated for version 2.
* Note that because of language differences, the numbers are slightly
* different from the Ada version.]
*
* The following program contains statements of a high level programming
* language (here: C) in a distribution considered representative:
*
* assignments 52 (51.0 %)
* control statements 33 (32.4 %)
* procedure, function calls 17 (16.7 %)
*
* 103 statements are dynamically executed. The program is balanced with
* respect to the three aspects:
*
* - statement type
* - operand type
* - operand locality
* operand global, local, parameter, or constant.
*
* The combination of these three aspects is balanced only approximately.
*
* 1. Statement Type:
* ----------------- number
*
* V1 = V2 9
* (incl. V1 = F(..)
* V = Constant 12
* Assignment, 7
* with array element
* Assignment, 6
* with record component
* --
* 34 34
*
* X = Y +|-|"&&"|"|" Z 5
* X = Y +|-|"==" Constant 6
* X = X +|- 1 3
* X = Y *|/ Z 2
* X = Expression, 1
* two operators
* X = Expression, 1
* three operators
* --
* 18 18
*
* if .... 14
* with "else" 7
* without "else" 7
* executed 3
* not executed 4
* for ... 7 | counted every time
* while ... 4 | the loop condition
* do ... while 1 | is evaluated
* switch ... 1
* break 1
* declaration with 1
* initialization
* --
* 34 34
*
* P (...) procedure call 11
* user procedure 10
* library procedure 1
* X = F (...)
* function call 6
* user function 5
* library function 1
* --
* 17 17
* ---
* 103
*
* The average number of parameters in procedure or function calls
* is 1.82 (not counting the function values aX *
*
* 2. Operators
* ------------
* number approximate
* percentage
*
* Arithmetic 32 50.8
*
* + 21 33.3
* - 7 11.1
* * 3 4.8
* / (int div) 1 1.6
*
* Comparison 27 42.8
*
* == 9 14.3
* /= 4 6.3
* > 1 1.6
* < 3 4.8
* >= 1 1.6
* <= 9 14.3
*
* Logic 4 6.3
*
* && (AND-THEN) 1 1.6
* | (OR) 1 1.6
* ! (NOT) 2 3.2
*
* -- -----
* 63 100.1
*
*
* 3. Operand Type (counted once per operand reference):
* ---------------
* number approximate
* percentage
*
* Integer 175 72.3 %
* Character 45 18.6 %
* Pointer 12 5.0 %
* String30 6 2.5 %
* Array 2 0.8 %
* Record 2 0.8 %
* --- -------
* 242 100.0 %
*
* When there is an access path leading to the final operand (e.g. a record
* component), only the final data type on the access path is counted.
*
*
* 4. Operand Locality:
* -------------------
* number approximate
* percentage
*
* local variable 114 47.1 %
* global variable 22 9.1 %
* parameter 45 18.6 %
* value 23 9.5 %
* reference 22 9.1 %
* function result 6 2.5 %
* constant 55 22.7 %
* --- -------
* 242 100.0 %
*
* The program does not compute anything meaningful, but it is syntactically
* and semantically correct. All variables have a value assigned to them
* before they are used as a source operand.
*
* There has been no explicit effort to account for the effects of a
* cache, or to balance the use of long or short displacements for code or
* data.
*
***************************************************************************
*/
/* Compiler and system dependent definitions: */
/* variables for time measurement: */
#ifdef TIME
#define CLOCK_TYPE "time()"
#undef HZ
#define HZ (1) /* time() returns time in seconds */
extern long time(); /* see library function "time" */
#define Too_Small_Time 2 /* Measurements should last at least 2 seconds */
#define Start_Timer() Begin_Time = time ( (long *) 0)
#define Stop_Timer() End_Time = time ( (long *) 0)
#else
#ifdef MSC_CLOCK /* Use Microsoft C hi-res clock */
#undef HZ
#undef TIMES
#include <time.h>
#ifdef CLOCKS_PER_SEC
#define HZ CLOCKS_PER_SEC
#define CLOCK_TYPE "STDC clock()"
#else
#define HZ CLK_TCK
#define CLOCK_TYPE "MSC clock()"
#endif
extern clock_t clock();
#define Too_Small_Time (2*HZ)
#define Start_Timer() Begin_Time = clock()
#define Stop_Timer() End_Time = clock()
#else
/* Use times(2) time function unless */
/* explicitly defined otherwise */
#define CLOCK_TYPE "times()"
#include <sys/types.h>
#include <sys/times.h>
#ifndef HZ /* Added by SP 900619 */
#include <sys/param.h> /* If your system doesn't have this, use -DHZ=xxx */
#else
*** You must define HZ!!! ***
#endif /* HZ */
#ifndef PASS2
struct tms time_info;
#endif
/*extern int times ();*/
/* see library function "times" */
#define Too_Small_Time (2*HZ)
/* Measurements should last at least about 2 seconds */
#define Start_Timer() times(&time_info); Begin_Time=(long)time_info.tms_utime
#define Stop_Timer() times(&time_info); End_Time = (long)time_info.tms_utime
#endif /* MSC_CLOCK */
#endif /* TIME */
#define Mic_secs_Per_Second 1000000.0
#ifdef FIXED_NUMBER_OF_PASSES
#define NUMBER_OF_RUNS FIXED_NUMBER_OF_PASSES
#else
#define NUMBER_OF_RUNS 50000 /* Default number of runs */
#endif
#ifdef NOSTRUCTASSIGN
#define structassign(d, s) memcpy(&(d), &(s), sizeof(d))
#else
#define structassign(d, s) d = s
#endif
#ifdef NOENUM
#define Ident_1 0
#define Ident_2 1
#define Ident_3 2
#define Ident_4 3
#define Ident_5 4
typedef int Enumeration;
#else
typedef enum {Ident_1, Ident_2, Ident_3, Ident_4, Ident_5}
Enumeration;
#endif
/* for boolean and enumeration types in Ada, Pascal */
/* General definitions: */
#include <stdio.h>
/* for strcpy, strcmp */
#define Null 0
/* Value of a Null pointer */
#define true 1
#define false 0
typedef int One_Thirty;
typedef int One_Fifty;
typedef char Capital_Letter;
typedef int Boolean;
typedef char Str_30 [31];
typedef int Arr_1_Dim [50];
typedef int Arr_2_Dim [50] [50];
typedef struct record
{
struct record *Ptr_Comp;
Enumeration Discr;
union {
struct {
Enumeration Enum_Comp;
int Int_Comp;
char Str_Comp [31];
} var_1;
struct {
Enumeration E_Comp_2;
char Str_2_Comp [31];
} var_2;
struct {
char Ch_1_Comp;
char Ch_2_Comp;
} var_3;
} variant;
} Rec_Type, *Rec_Pointer;
#ifndef PASS2
/* Global Variables: */
Rec_Pointer Ptr_Glob,
Next_Ptr_Glob;
int Int_Glob;
Boolean Bool_Glob;
char Ch_1_Glob,
Ch_2_Glob;
int Arr_1_Glob [50];
int Arr_2_Glob [50] [50];
extern char *malloc ();
Enumeration Func_1 ();
/* forward declaration necessary since Enumeration may not simply be int */
#ifndef REG
Boolean Reg = false;
#define REG
/* REG becomes defined as empty */
/* i.e. no register variables */
#else
Boolean Reg = true;
#undef REG
#define REG register
#endif
Boolean Done;
long Begin_Time,
End_Time,
User_Time;
#ifdef INTEGER_ONLY
long long Microseconds,
Dhrystones_Per_Second;
#else
float Microseconds,
Dhrystones_Per_Second;
#endif
/* end of variables for time measurement */
main (argc, argv) int argc; char *argv[];
/*****/
/* main program, corresponds to procedures */
/* Main and Proc_0 in the Ada version */
{
One_Fifty Int_1_Loc;
REG One_Fifty Int_2_Loc;
One_Fifty Int_3_Loc;
REG char Ch_Index;
Enumeration Enum_Loc;
Str_30 Str_1_Loc;
Str_30 Str_2_Loc;
REG int Run_Index;
REG int Number_Of_Runs;
#if !defined(FIXED_NUMBER_OF_PASSES)
/* Arguments */
if (argc > 2)
{
printf ("Usage: %s [number of loops]\n", argv[0]);
exit (1);
}
if (argc == 2)
{
Number_Of_Runs = atoi (argv[1]);
} else
{
Number_Of_Runs = NUMBER_OF_RUNS;
}
if (Number_Of_Runs <= 0)
#endif
{
Number_Of_Runs = NUMBER_OF_RUNS;
}
/* Initializations */
Next_Ptr_Glob = (Rec_Pointer) malloc (sizeof (Rec_Type));
Ptr_Glob = (Rec_Pointer) malloc (sizeof (Rec_Type));
Ptr_Glob->Ptr_Comp = Next_Ptr_Glob;
Ptr_Glob->Discr = Ident_1;
Ptr_Glob->variant.var_1.Enum_Comp = Ident_3;
Ptr_Glob->variant.var_1.Int_Comp = 40;
strcpy (Ptr_Glob->variant.var_1.Str_Comp,
"DHRYSTONE PROGRAM, SOME STRING");
strcpy (Str_1_Loc, "DHRYSTONE PROGRAM, 1'ST STRING");
Arr_2_Glob [8][7] = 10;
/* Was missing in published program. Without this statement, */
/* Arr_2_Glob [8][7] would have an undefined value. */
/* Warning: With 16-Bit processors and Number_Of_Runs > 32000, */
/* overflow may occur for this array element. */
printf ("\n");
printf ("Dhrystone Benchmark, Version %s\n", Version);
if (Reg)
{
printf ("Program compiled with 'register' attribute\n");
}
else
{
printf ("Program compiled without 'register' attribute\n");
}
printf ("Using %s, HZ=%d\n", CLOCK_TYPE, HZ);
printf ("\n");
Done = false;
while (!Done) {
printf ("Trying %d runs through Dhrystone:\n", Number_Of_Runs);
/***************/
/* Start timer */
/***************/
Start_Timer();
for (Run_Index = 1; Run_Index <= Number_Of_Runs; ++Run_Index)
{
Proc_5();
Proc_4();
/* Ch_1_Glob == 'A', Ch_2_Glob == 'B', Bool_Glob == true */
Int_1_Loc = 2;
Int_2_Loc = 3;
strcpy (Str_2_Loc, "DHRYSTONE PROGRAM, 2'ND STRING");
Enum_Loc = Ident_2;
Bool_Glob = ! Func_2 (Str_1_Loc, Str_2_Loc);
/* Bool_Glob == 1 */
while (Int_1_Loc < Int_2_Loc) /* loop body executed once */
{
Int_3_Loc = 5 * Int_1_Loc - Int_2_Loc;
/* Int_3_Loc == 7 */
Proc_7 (Int_1_Loc, Int_2_Loc, &Int_3_Loc);
/* Int_3_Loc == 7 */
Int_1_Loc += 1;
} /* while */
/* Int_1_Loc == 3, Int_2_Loc == 3, Int_3_Loc == 7 */
Proc_8 (Arr_1_Glob, Arr_2_Glob, Int_1_Loc, Int_3_Loc);
/* Int_Glob == 5 */
Proc_1 (Ptr_Glob);
for (Ch_Index = 'A'; Ch_Index <= Ch_2_Glob; ++Ch_Index)
/* loop body executed twice */
{
if (Enum_Loc == Func_1 (Ch_Index, 'C'))
/* then, not executed */
{
Proc_6 (Ident_1, &Enum_Loc);
strcpy (Str_2_Loc, "DHRYSTONE PROGRAM, 3'RD STRING");
Int_2_Loc = Run_Index;
Int_Glob = Run_Index;
}
}
/* Int_1_Loc == 3, Int_2_Loc == 3, Int_3_Loc == 7 */
Int_2_Loc = Int_2_Loc * Int_1_Loc;
Int_1_Loc = Int_2_Loc / Int_3_Loc;
Int_2_Loc = 7 * (Int_2_Loc - Int_3_Loc) - Int_1_Loc;
/* Int_1_Loc == 1, Int_2_Loc == 13, Int_3_Loc == 7 */
Proc_2 (&Int_1_Loc);
/* Int_1_Loc == 5 */
} /* loop "for Run_Index" */
/**************/
/* Stop timer */
/**************/
Stop_Timer();
User_Time = End_Time - Begin_Time;
if (User_Time < Too_Small_Time)
{
printf ("Measured time too small to obtain meaningful results\n");
Number_Of_Runs = Number_Of_Runs * 10;
printf ("\n");
} else Done = true;
}
printf ("Final values of the variables used in the benchmark:\n");
printf ("\n");
printf ("Int_Glob: %d\n", Int_Glob);
printf (" should be: %d\n", 5);
printf ("Bool_Glob: %d\n", Bool_Glob);
printf (" should be: %d\n", 1);
printf ("Ch_1_Glob: %c\n", Ch_1_Glob);
printf (" should be: %c\n", 'A');
printf ("Ch_2_Glob: %c\n", Ch_2_Glob);
printf (" should be: %c\n", 'B');
printf ("Arr_1_Glob[8]: %d\n", Arr_1_Glob[8]);
printf (" should be: %d\n", 7);
printf ("Arr_2_Glob[8][7]: %d\n", Arr_2_Glob[8][7]);
printf (" should be: Number_Of_Runs + 10\n");
printf ("Ptr_Glob->\n");
printf (" Ptr_Comp: %d\n", (int) Ptr_Glob->Ptr_Comp);
printf (" should be: (implementation-dependent)\n");
printf (" Discr: %d\n", Ptr_Glob->Discr);
printf (" should be: %d\n", 0);
printf (" Enum_Comp: %d\n", Ptr_Glob->variant.var_1.Enum_Comp);
printf (" should be: %d\n", 2);
printf (" Int_Comp: %d\n", Ptr_Glob->variant.var_1.Int_Comp);
printf (" should be: %d\n", 17);
printf (" Str_Comp: %s\n", Ptr_Glob->variant.var_1.Str_Comp);
printf (" should be: DHRYSTONE PROGRAM, SOME STRING\n");
printf ("Next_Ptr_Glob->\n");
printf (" Ptr_Comp: %d\n", (int) Next_Ptr_Glob->Ptr_Comp);
printf (" should be: (implementation-dependent), same as above\n");
printf (" Discr: %d\n", Next_Ptr_Glob->Discr);
printf (" should be: %d\n", 0);
printf (" Enum_Comp: %d\n", Next_Ptr_Glob->variant.var_1.Enum_Comp);
printf (" should be: %d\n", 1);
printf (" Int_Comp: %d\n", Next_Ptr_Glob->variant.var_1.Int_Comp);
printf (" should be: %d\n", 18);
printf (" Str_Comp: %s\n",
Next_Ptr_Glob->variant.var_1.Str_Comp);
printf (" should be: DHRYSTONE PROGRAM, SOME STRING\n");
printf ("Int_1_Loc: %d\n", Int_1_Loc);
printf (" should be: %d\n", 5);
printf ("Int_2_Loc: %d\n", Int_2_Loc);
printf (" should be: %d\n", 13);
printf ("Int_3_Loc: %d\n", Int_3_Loc);
printf (" should be: %d\n", 7);
printf ("Enum_Loc: %d\n", Enum_Loc);
printf (" should be: %d\n", 1);
printf ("Str_1_Loc: %s\n", Str_1_Loc);
printf (" should be: DHRYSTONE PROGRAM, 1'ST STRING\n");
printf ("Str_2_Loc: %s\n", Str_2_Loc);
printf (" should be: DHRYSTONE PROGRAM, 2'ND STRING\n");
printf ("\n");
#ifdef INTEGER_ONLY
Microseconds = (long long) User_Time * 1000000LL
/ ((long long) HZ * ((long long) Number_Of_Runs));
Dhrystones_Per_Second = ((long long) HZ * (long long) Number_Of_Runs)
/ (long long) User_Time;
printf ("Microseconds for one run through Dhrystone: ");
printf ("%u \n", (unsigned)Microseconds);
printf ("Dhrystones per Second: ");
printf ("%u \n", (unsigned)Dhrystones_Per_Second);
printf ("\n");
#else
Microseconds = (float) User_Time * Mic_secs_Per_Second
/ ((float) HZ * ((float) Number_Of_Runs));
Dhrystones_Per_Second = ((float) HZ * (float) Number_Of_Runs)
/ (float) User_Time;
printf ("Microseconds for one run through Dhrystone: ");
printf ("%10.1f \n", Microseconds);
printf ("Dhrystones per Second: ");
printf ("%10.0f \n", Dhrystones_Per_Second);
printf ("\n");
#endif
}
Proc_1 (Ptr_Val_Par)
/******************/
REG Rec_Pointer Ptr_Val_Par;
/* executed once */
{
REG Rec_Pointer Next_Record = Ptr_Val_Par->Ptr_Comp;
/* == Ptr_Glob_Next */
/* Local variable, initialized with Ptr_Val_Par->Ptr_Comp, */
/* corresponds to "rename" in Ada, "with" in Pascal */
structassign (*Ptr_Val_Par->Ptr_Comp, *Ptr_Glob);
Ptr_Val_Par->variant.var_1.Int_Comp = 5;
Next_Record->variant.var_1.Int_Comp
= Ptr_Val_Par->variant.var_1.Int_Comp;
Next_Record->Ptr_Comp = Ptr_Val_Par->Ptr_Comp;
Proc_3 (&Next_Record->Ptr_Comp);
/* Ptr_Val_Par->Ptr_Comp->Ptr_Comp
== Ptr_Glob->Ptr_Comp */
if (Next_Record->Discr == Ident_1)
/* then, executed */
{
Next_Record->variant.var_1.Int_Comp = 6;
Proc_6 (Ptr_Val_Par->variant.var_1.Enum_Comp,
&Next_Record->variant.var_1.Enum_Comp);
Next_Record->Ptr_Comp = Ptr_Glob->Ptr_Comp;
Proc_7 (Next_Record->variant.var_1.Int_Comp, 10,
&Next_Record->variant.var_1.Int_Comp);
}
else /* not executed */
structassign (*Ptr_Val_Par, *Ptr_Val_Par->Ptr_Comp);
} /* Proc_1 */
Proc_2 (Int_Par_Ref)
/******************/
/* executed once */
/* *Int_Par_Ref == 1, becomes 4 */
One_Fifty *Int_Par_Ref;
{
One_Fifty Int_Loc;
Enumeration Enum_Loc;
Int_Loc = *Int_Par_Ref + 10;
do /* executed once */
if (Ch_1_Glob == 'A')
/* then, executed */
{
Int_Loc -= 1;
*Int_Par_Ref = Int_Loc - Int_Glob;
Enum_Loc = Ident_1;
} /* if */
while (Enum_Loc != Ident_1); /* true */
} /* Proc_2 */
Proc_3 (Ptr_Ref_Par)
/******************/
/* executed once */
/* Ptr_Ref_Par becomes Ptr_Glob */
Rec_Pointer *Ptr_Ref_Par;
{
if (Ptr_Glob != Null)
/* then, executed */
*Ptr_Ref_Par = Ptr_Glob->Ptr_Comp;
Proc_7 (10, Int_Glob, &Ptr_Glob->variant.var_1.Int_Comp);
} /* Proc_3 */
Proc_4 () /* without parameters */
/*******/
/* executed once */
{
Boolean Bool_Loc;
Bool_Loc = Ch_1_Glob == 'A';
Bool_Glob = Bool_Loc | Bool_Glob;
Ch_2_Glob = 'B';
} /* Proc_4 */
Proc_5 () /* without parameters */
/*******/
/* executed once */
{
Ch_1_Glob = 'A';
Bool_Glob = false;
} /* Proc_5 */
/* Procedure for the assignment of structures, */
/* if the C compiler doesn't support this feature */
#ifdef NOSTRUCTASSIGN
memcpy (d, s, l)
register char *d;
register char *s;
register int l;
{
while (l--) *d++ = *s++;
}
#endif
#else /* PASS2 */
#ifndef REG
#define REG
/* REG becomes defined as empty */
/* i.e. no register variables */
#else
#undef REG
#define REG register
#endif
extern int Int_Glob;
extern char Ch_1_Glob;
Proc_6 (Enum_Val_Par, Enum_Ref_Par)
/*********************************/
/* executed once */
/* Enum_Val_Par == Ident_3, Enum_Ref_Par becomes Ident_2 */
Enumeration Enum_Val_Par;
Enumeration *Enum_Ref_Par;
{
*Enum_Ref_Par = Enum_Val_Par;
if (! Func_3 (Enum_Val_Par))
/* then, not executed */
*Enum_Ref_Par = Ident_4;
switch (Enum_Val_Par)
{
case Ident_1:
*Enum_Ref_Par = Ident_1;
break;
case Ident_2:
if (Int_Glob > 100)
/* then */
*Enum_Ref_Par = Ident_1;
else *Enum_Ref_Par = Ident_4;
break;
case Ident_3: /* executed */
*Enum_Ref_Par = Ident_2;
break;
case Ident_4: break;
case Ident_5:
*Enum_Ref_Par = Ident_3;
break;
} /* switch */
} /* Proc_6 */
Proc_7 (Int_1_Par_Val, Int_2_Par_Val, Int_Par_Ref)
/**********************************************/
/* executed three times */
/* first call: Int_1_Par_Val == 2, Int_2_Par_Val == 3, */
/* Int_Par_Ref becomes 7 */
/* second call: Int_1_Par_Val == 10, Int_2_Par_Val == 5, */
/* Int_Par_Ref becomes 17 */
/* third call: Int_1_Par_Val == 6, Int_2_Par_Val == 10, */
/* Int_Par_Ref becomes 18 */
One_Fifty Int_1_Par_Val;
One_Fifty Int_2_Par_Val;
One_Fifty *Int_Par_Ref;
{
One_Fifty Int_Loc;
Int_Loc = Int_1_Par_Val + 2;
*Int_Par_Ref = Int_2_Par_Val + Int_Loc;
} /* Proc_7 */
Proc_8 (Arr_1_Par_Ref, Arr_2_Par_Ref, Int_1_Par_Val, Int_2_Par_Val)
/*********************************************************************/
/* executed once */
/* Int_Par_Val_1 == 3 */
/* Int_Par_Val_2 == 7 */
Arr_1_Dim Arr_1_Par_Ref;
Arr_2_Dim Arr_2_Par_Ref;
int Int_1_Par_Val;
int Int_2_Par_Val;
{
REG One_Fifty Int_Index;
REG One_Fifty Int_Loc;
Int_Loc = Int_1_Par_Val + 5;
Arr_1_Par_Ref [Int_Loc] = Int_2_Par_Val;
Arr_1_Par_Ref [Int_Loc+1] = Arr_1_Par_Ref [Int_Loc];
Arr_1_Par_Ref [Int_Loc+30] = Int_Loc;
for (Int_Index = Int_Loc; Int_Index <= Int_Loc+1; ++Int_Index)
Arr_2_Par_Ref [Int_Loc] [Int_Index] = Int_Loc;
Arr_2_Par_Ref [Int_Loc] [Int_Loc-1] += 1;
Arr_2_Par_Ref [Int_Loc+20] [Int_Loc] = Arr_1_Par_Ref [Int_Loc];
Int_Glob = 5;
} /* Proc_8 */
Enumeration Func_1 (Ch_1_Par_Val, Ch_2_Par_Val)
/*************************************************/
/* executed three times */
/* first call: Ch_1_Par_Val == 'H', Ch_2_Par_Val == 'R' */
/* second call: Ch_1_Par_Val == 'A', Ch_2_Par_Val == 'C' */
/* third call: Ch_1_Par_Val == 'B', Ch_2_Par_Val == 'C' */
Capital_Letter Ch_1_Par_Val;
Capital_Letter Ch_2_Par_Val;
{
Capital_Letter Ch_1_Loc;
Capital_Letter Ch_2_Loc;
Ch_1_Loc = Ch_1_Par_Val;
Ch_2_Loc = Ch_1_Loc;
if (Ch_2_Loc != Ch_2_Par_Val)
/* then, executed */
return (Ident_1);
else /* not executed */
{
Ch_1_Glob = Ch_1_Loc;
return (Ident_2);
}
} /* Func_1 */
Boolean Func_2 (Str_1_Par_Ref, Str_2_Par_Ref)
/*************************************************/
/* executed once */
/* Str_1_Par_Ref == "DHRYSTONE PROGRAM, 1'ST STRING" */
/* Str_2_Par_Ref == "DHRYSTONE PROGRAM, 2'ND STRING" */
Str_30 Str_1_Par_Ref;
Str_30 Str_2_Par_Ref;
{
REG One_Thirty Int_Loc;
Capital_Letter Ch_Loc;
Int_Loc = 2;
while (Int_Loc <= 2) /* loop body executed once */
if (Func_1 (Str_1_Par_Ref[Int_Loc],
Str_2_Par_Ref[Int_Loc+1]) == Ident_1)
/* then, executed */
{
Ch_Loc = 'A';
Int_Loc += 1;
} /* if, while */
if (Ch_Loc >= 'W' && Ch_Loc < 'Z')
/* then, not executed */
Int_Loc = 7;
if (Ch_Loc == 'R')
/* then, not executed */
return (true);
else /* executed */
{
if (strcmp (Str_1_Par_Ref, Str_2_Par_Ref) > 0)
/* then, not executed */
{
Int_Loc += 7;
Int_Glob = Int_Loc;
return (true);
}
else /* executed */
return (false);
} /* if Ch_Loc */
} /* Func_2 */
Boolean Func_3 (Enum_Par_Val)
/***************************/
/* executed once */
/* Enum_Par_Val == Ident_3 */
Enumeration Enum_Par_Val;
{
Enumeration Enum_Loc;
Enum_Loc = Enum_Par_Val;
if (Enum_Loc == Ident_3)
/* then, executed */
return (true);
else /* not executed */
return (false);
} /* Func_3 */
#endif /* PASS2 */
There are no modifications. Show me where they are different.
Well, for a start, you might want to check out the version on Stephen Pemberton's home page - he helped write the benchmark in the first place. But then, what would he know?
I'm sure GCC will achieve compliance with the C standard - Catalina has demonstrated that this is an entirely realistic goal even on the Propelle.r
I don't want to get into this argument about which compiler is better but I would like to ask a question and it's not so that I can come back with an "ours is better than yours" response! Catalina may very well have some better ideas (probably does) and I think we could all benefit more by sharing than attacking each other.
Ross,
You say that Catalina has demonstrated that it is possible to achieve full C standard compliance on the Propeller. I'm curious as to whether you've been able to implement a compliant library that will allow a significant size program to run entirely within hub memory. This has been something we've struggled with on the GCC project. We have newlib partially ported and it implements a fairly complete library but it isn't at all practical for running code from hub memory. It works fine, of course, from external memory but I think most people use the Propeller without the benefit of a large external flash or SRAM. In order to make the library work well in a hub-only mode we've somewhat simplified the I/O model. Have you been able to achieve full compliance while still supporting significant hub-only development without making any compromises?
I don't want to get into this argument about which compiler is better but I would like to ask a question and it's not so that I can come back with an "ours is better than yours" response! Catalina may very well have some better ideas (probably does) and I think we could all benefit more by sharing than attacking each other.
Ross,
You say that Catalina has demonstrated that it is possible to achieve full C standard compliance on the Propeller. I'm curious as to whether you've been able to implement a compliant library that will allow a significant size program to run entirely within hub memory. This has been something we've struggled with on the GCC project. We have newlib partially ported and it implements a fairly complete library but it isn't at all practical for running code from hub memory. It works fine, of course, from external memory but I think most people use the Propeller without the benefit of a large external flash or SRAM. In order to make the library work well in a hub-only mode we've somewhat simplified the I/O model. Have you been able to achieve full compliance while still supporting significant hub-only development without making any compromises?
Thanks,
David
Hi David,
I agree this argument is getting a bit silly. I'm perfectly happy to leave it be and move on. We all have better ways to spend our time - or at least I do!
So to your point - can Catalina be used to create a "significant size" program that can execute in Hub RAM and have access to a fully compliant C library? Well, the answer is a qualified "yes". I say qualified because there are indeed some limitations. While there aren't any standard C library functions I can't call from a program executing from Hub RAM, it is true that if (for example) I use the standard C file I/O functions then I can't write a Hub RAM program that does much more than opens a file, reads from it and spits out the contents. Much more than that and I simply run out of space. Is that a "significant size" program? - well it is if you understand what is actually involved in doing it - but it probably isn't from a user perspective.
The main problem is that the Catalina library is based on the Amsterdam Compiler Kit (ACK) library, augmented by DOSFS - both of which are written entirely in C (except for some stuff I had to add). This makes them very portable, but they are definitely not very efficient. If more of the library functions were rewritten in PASM, then I think the answer to your question would be a definite "yes" - but this is quite a significant undertaking. I originally had planned to work on this as I had time, but now I'm happy to leave this to the team who are getting paid to do that kind of thing. I'd rather wait for the Prop II to make this particular problem go away, Another possible alternative would be to simply "buy" a better C library - there are several commercial ones available that could do a much better job - but I don't have money to burn, and there is ample evidence that no-one is going to make money trying to sell a compiler for the Propeller - so ACK and DOSFS come at the right price!
By the way, I did look at NewLib (it seemed an obvious choice) but I decided it was far too cumbersome to be useful. You find statements like this about NewLib (quote taken from here):
"The classic, "Hello, world!" test application occupies less than 30k in a newlib-based Linux runtime environment; the equivalent glibc-based application is more than 380k in size."
Huh???? 30k???? And that on top of Linux???? What kind of fantasy land are these people living in? This is the kind of "embedded" that you often hear people talking about when what they really mean is a full-blown Windows or Linux environment - just housed in a box without a screen and keyboard. That ain't my definition of "embedded"! With ACK a "hello, world" comes in at a mere fraction of that size.
In summary, as long as the expensive library functions are used judiciously (and Catalina does provide less expensive alternatives to all of them) then Catalina can certainly be used for both interesting and useful programs that can be executed entirely in Hub RAM - programs of about of the same order of size and complexity as most Spin programs, but in a language that is a lot more accessible for many people than Spin.
Thanks for your response! Actually, it was file I/O that was giving us the most trouble and we're using dosfs.c as well. Did you do anything to trim down the dosfs.c code? I've thought that it might be helpful to split it into several files so, for instance, you don't end up linking in file write code when you're only reading files. I haven't had time to try that yet though.
Thanks,
David
P.S. I intend to try Catalina 3.4 soon. I tried compiling it on the Mac but wasn't able to get it completely working. I'll install it on my Windows 7 machine once our power comes back on again. :-)
The main problem is that the Catalina library is based on the Amsterdam Compiler Kit (ACK) library, augmented by DOSFS - both of which are written entirely in C (except for some stuff I had to add). This makes them very portable, but they are definitely not very efficient.
You are welcome to use our standard C library which is designed to be small. I believe we could all benefit from that.
Actually, it was file I/O that was giving us the most trouble and we're using dosfs.c as well. Did you do anything to trim down the dosfs.c code? I've thought that it might be helpful to split it into several files so, for instance, you don't end up linking in file write code when you're only reading files. I haven't had time to try that yet though.
David,
I was able to get my filetest program to run under LMM. I've been trying to check it into the respository, but I haven't had much success. I'll post it to the PropGCC forum when I have a chance.
I had to used a stripped down version of printf to get it to fit. I also reduced the size of the file cache buffers from 512 to 32 bytes. Of course, the scratch sector buffer still has to be 512 bytes in size.
Thanks for your response! Actually, it was file I/O that was giving us the most trouble and we're using dosfs.c as well. Did you do anything to trim down the dosfs.c code? I've thought that it might be helpful to split it into several files so, for instance, you don't end up linking in file write code when you're only reading files. I haven't had time to try that yet though.
Thanks,
David
P.S. I intend to try Catalina 3.4 soon. I tried compiling it on the Mac but wasn't able to get it completely working. I'll install it on my Windows 7 machine once our power comes back on again. :-)
Hi David,
Yes, I think I remember I had to split DOSFS into several source files. This certainly helps. Other than that, the only significant change I made was to remove support for FAT12 (you can re-include it by recompiling the library if you need it). Now it only supports FAT32 and FAT16 by default. This saves a few bytes. I have also been thinking about reducing the number of file handles, since it seems unlikely that anyone will ever need to open 20 files on the Prop at once!
One request though - if you want to continue to discuss GCC issues, can you please continue this discussion in another thread? This thread is about a specific version of Catalina (one which has now been superseded) and this discussion is a bit off-topic.
Comments
Yes, and I'll take your benchmark results seriously when you can compile the original benchmark sources, using full versions of the C libraries.
You know, the silly thing is that you didn't really need to resort to trickery to get good benchmark results - I'd be happy to acknowledge that GCC has a better optimizer than Catalina, and can produce faster code. On the other hand, Catalina produces smaller code than GCC, and so can fit the original benchmarks comfortably in Hub RAM without requiring them to be modified.
Ross.
There are no modifications. Show me where they are different.
Which "full C libraries" do you want to use for comparison?
Our libraries are designed to fit the needs of my primary customer.
There is more than one way to achieve the goal.
I'm sure GCC will achieve compliance with the C standard - Catalina has demonstrated that this is an entirely realistic goal even on the Propeller.
And then we can talk benchmark results
Ross.
Ross,
You say that Catalina has demonstrated that it is possible to achieve full C standard compliance on the Propeller. I'm curious as to whether you've been able to implement a compliant library that will allow a significant size program to run entirely within hub memory. This has been something we've struggled with on the GCC project. We have newlib partially ported and it implements a fairly complete library but it isn't at all practical for running code from hub memory. It works fine, of course, from external memory but I think most people use the Propeller without the benefit of a large external flash or SRAM. In order to make the library work well in a hub-only mode we've somewhat simplified the I/O model. Have you been able to achieve full compliance while still supporting significant hub-only development without making any compromises?
Thanks,
David
Hi David,
I agree this argument is getting a bit silly. I'm perfectly happy to leave it be and move on. We all have better ways to spend our time - or at least I do!
So to your point - can Catalina be used to create a "significant size" program that can execute in Hub RAM and have access to a fully compliant C library? Well, the answer is a qualified "yes". I say qualified because there are indeed some limitations. While there aren't any standard C library functions I can't call from a program executing from Hub RAM, it is true that if (for example) I use the standard C file I/O functions then I can't write a Hub RAM program that does much more than opens a file, reads from it and spits out the contents. Much more than that and I simply run out of space. Is that a "significant size" program? - well it is if you understand what is actually involved in doing it - but it probably isn't from a user perspective.
The main problem is that the Catalina library is based on the Amsterdam Compiler Kit (ACK) library, augmented by DOSFS - both of which are written entirely in C (except for some stuff I had to add). This makes them very portable, but they are definitely not very efficient. If more of the library functions were rewritten in PASM, then I think the answer to your question would be a definite "yes" - but this is quite a significant undertaking. I originally had planned to work on this as I had time, but now I'm happy to leave this to the team who are getting paid to do that kind of thing. I'd rather wait for the Prop II to make this particular problem go away, Another possible alternative would be to simply "buy" a better C library - there are several commercial ones available that could do a much better job - but I don't have money to burn, and there is ample evidence that no-one is going to make money trying to sell a compiler for the Propeller - so ACK and DOSFS come at the right price!
By the way, I did look at NewLib (it seemed an obvious choice) but I decided it was far too cumbersome to be useful. You find statements like this about NewLib (quote taken from here):
Huh???? 30k???? And that on top of Linux???? What kind of fantasy land are these people living in? This is the kind of "embedded" that you often hear people talking about when what they really mean is a full-blown Windows or Linux environment - just housed in a box without a screen and keyboard. That ain't my definition of "embedded"! With ACK a "hello, world" comes in at a mere fraction of that size.
In summary, as long as the expensive library functions are used judiciously (and Catalina does provide less expensive alternatives to all of them) then Catalina can certainly be used for both interesting and useful programs that can be executed entirely in Hub RAM - programs of about of the same order of size and complexity as most Spin programs, but in a language that is a lot more accessible for many people than Spin.
Ross.
Thanks for your response! Actually, it was file I/O that was giving us the most trouble and we're using dosfs.c as well. Did you do anything to trim down the dosfs.c code? I've thought that it might be helpful to split it into several files so, for instance, you don't end up linking in file write code when you're only reading files. I haven't had time to try that yet though.
Thanks,
David
P.S. I intend to try Catalina 3.4 soon. I tried compiling it on the Mac but wasn't able to get it completely working. I'll install it on my Windows 7 machine once our power comes back on again. :-)
You are welcome to use our standard C library which is designed to be small. I believe we could all benefit from that.
I was able to get my filetest program to run under LMM. I've been trying to check it into the respository, but I haven't had much success. I'll post it to the PropGCC forum when I have a chance.
I had to used a stripped down version of printf to get it to fit. I also reduced the size of the file cache buffers from 512 to 32 bytes. Of course, the scratch sector buffer still has to be 512 bytes in size.
Dave
Hi David,
Yes, I think I remember I had to split DOSFS into several source files. This certainly helps. Other than that, the only significant change I made was to remove support for FAT12 (you can re-include it by recompiling the library if you need it). Now it only supports FAT32 and FAT16 by default. This saves a few bytes. I have also been thinking about reducing the number of file handles, since it seems unlikely that anyone will ever need to open 20 files on the Prop at once!
One request though - if you want to continue to discuss GCC issues, can you please continue this discussion in another thread? This thread is about a specific version of Catalina (one which has now been superseded) and this discussion is a bit off-topic.
Thanks,
Ross.