English Amiga Board


Go Back   English Amiga Board > Coders > Coders. Language > Coders. C/C++

 
 
Thread Tools
Old 21 September 2023, 07:32   #1
Bruce Abbott
Registered User
 
Bruce Abbott's Avatar
 
Join Date: Mar 2018
Location: Hastings, New Zealand
Posts: 2,584
Using GenProto in SASC

I'm trying to compile a "DHRYSTONE" benchmark program written in 1984 that I found on Fish disk #378. When I turn register parameters on SASC says it can't do that because there are no 'prototypes' for the functions involved.

That sort of makes sense, so I use the 'GenProto' compile option to create a file that looks like this:-
Code:
/* Prototypes for functions defined in
dry.c
 */

int main(void);

extern int IntGlob;

extern boolean BoolGlob;

extern char Char1Glob;

extern char Char2Glob;

extern Array1Dim Array1Glob;

extern Array2Dim Array2Glob;

extern RecordPtr PtrGlb;

extern RecordPtr PtrGlbNext;

int Proc0(void);

int Proc1(RecordPtr PtrParIn);

int Proc2(OneToFifty * IntParIO);

int Proc3(RecordPtr * PtrParOut);

int Proc4(void);

int Proc5(void);

int Proc6(Enumeration EnumParIn,
          Enumeration * EnumParOut);

int Proc7(OneToFifty IntParI1,
          OneToFifty IntParI2,
          OneToFifty * IntParOut);

int Proc8(Array1Dim Array1Par,
          Array2Dim Array2Par,
          OneToFifty IntParI1,
          OneToFifty IntParI2);

Enumeration Func1(CapitalLetter CharPar1,
                  CapitalLetter CharPar2);

boolean Func2(String30 StrParI1,
              String30 StrParI2);

boolean Func3(Enumeration EnumParIn);
I include this file at the start after <stdio.h>, and then the compiler throws up a heap of errors about things like 'semi-colon expected', 'comma expected' and 'invalid function definition'. What am I doing wrong?

Here's the program code:-
Code:
#include <stdio.h>
#include "dry_protos.h"
/*
 *   "DHRYSTONE" Benchmark Program
 *
 */
 
/* Accuracy of timings and human fatigue controlled by next two lines */
#define LOOPS   50000      /* Use this for slow or 16 bit machines */
/*#define LOOPS   500000      /* Use this for faster machines */

/* Compiler dependent options */
#define   NOENUM         /* Define if compiler has no enum's */
#undef   NOSTRUCTASSIGN      /* Define if compiler can't assign structures */
/* define only one of the next three defines */
/*#define NOTIME      /* Define if no time() function in library */
/*#define TIMES         /* Use times(2) time function */
/*#define TIME         /* Use time(2) time function */

/* define the granularity of your times(2) function (when used) */
#define HZ   60      /* times(2) returns 1/60 second (most) */
/*#define HZ   100      /* times(2) returns 1/100 second (WECo) */

#ifdef   NOSTRUCTASSIGN
#define   structassign(d, s)   memcpy(&(d), &(s), sizeof(d))
#else
#define   structassign(d, s)   d = s
#endif

#ifdef   NOENUM
#define   Ident1   1
#define   Ident2   2
#define   Ident3   3
#define   Ident4   4
#define   Ident5   5
typedef int   Enumeration;
#else
typedef enum   {Ident1, Ident2, Ident3, Ident4, Ident5} Enumeration;
#endif

typedef int   OneToThirty;
typedef int   OneToFifty;
typedef char   CapitalLetter;
typedef char   String30[31];
typedef int   Array1Dim[51];
typedef int   Array2Dim[51][51];

struct   Record
{
   struct Record      *PtrComp;
   Enumeration      Discr;
   Enumeration      EnumComp;
   OneToFifty      IntComp;
   String30      StringComp;
};

typedef struct Record    RecordType;
typedef RecordType *   RecordPtr;
typedef int      boolean;

#define   NULL      0
#define   TRUE      1
#define   FALSE      0

#ifndef REG
#define   REG
#endif

extern Enumeration   Func1();
extern boolean      Func2();

#ifdef TIMES
#include <sys/types.h>
#include <sys/times.h>
#endif

main()
{
   Proc0();
}

/*
 * Package 1
 */
int      IntGlob;
boolean      BoolGlob;
char      Char1Glob;
char      Char2Glob;
Array1Dim   Array1Glob;
Array2Dim   Array2Glob;
RecordPtr   PtrGlb;
RecordPtr   PtrGlbNext;

Proc0()
{
   OneToFifty      IntLoc1;
   REG OneToFifty      IntLoc2;
   OneToFifty      IntLoc3;
   REG char      CharLoc;
   REG char      CharIndex;
   Enumeration       EnumLoc;
   String30      String1Loc;
   String30      String2Loc;
   register unsigned int   i;

#ifdef TIME
   long         time();
   long         starttime;
   long         benchtime;
   long         nulltime;
   starttime = time(0);
   for (i = 0; i < LOOPS; ++i);
   nulltime = time(0) - starttime; /* Computes overhead of looping */
#endif
#ifdef TIMES
   time_t         starttime;
   time_t         benchtime;
   time_t         nulltime;
   struct tms      tms;
   times(&tms); starttime = tms.tms_utime;
   for (i = 0; i < LOOPS; ++i);
   times(&tms);
   nulltime = tms.tms_utime - starttime; /* Computes overhead of looping */
#endif

   PtrGlbNext = (RecordPtr) malloc(sizeof(RecordType));
   PtrGlb = (RecordPtr) malloc(sizeof(RecordType));
   PtrGlb->PtrComp = PtrGlbNext;
   PtrGlb->Discr = Ident1;
   PtrGlb->EnumComp = Ident3;
   PtrGlb->IntComp = 40;
   strcpy(PtrGlb->StringComp, "DHRYSTONE PROGRAM, SOME STRING");

/*****************
-- Start Timer --
*****************/
   printf ("Start timer!\n");
   fflush (stdout);
#ifdef TIME
   starttime = time(0);
#endif
#ifdef TIMES
   times(&tms); starttime = tms.tms_utime;
#endif
for (i = 0; i < LOOPS; ++i)
{

   Proc5();
   Proc4();
   IntLoc1 = 2;
   IntLoc2 = 3;
   strcpy(String2Loc, "DHRYSTONE PROGRAM, 2'ND STRING");
   EnumLoc = Ident2;
   BoolGlob = ! Func2(String1Loc, String2Loc);
   while (IntLoc1 < IntLoc2)
   {
      IntLoc3 = 5 * IntLoc1 - IntLoc2;
      Proc7(IntLoc1, IntLoc2, &IntLoc3);
      ++IntLoc1;
   }
   Proc8(Array1Glob, Array2Glob, IntLoc1, IntLoc3);
   Proc1(PtrGlb);
   for (CharIndex = 'A'; CharIndex <= Char2Glob; ++CharIndex)
      if (EnumLoc == Func1(CharIndex, 'C'))
         Proc6(Ident1, &EnumLoc);
   IntLoc3 = IntLoc2 * IntLoc1;
   IntLoc2 = IntLoc3 / IntLoc1;
   IntLoc2 = 7 * (IntLoc3 - IntLoc2) - IntLoc1;
   Proc2(&IntLoc1);

/*****************
-- Stop Timer --
*****************/
}
   printf ("Stop timer!\n");
   fflush (stdout);
#ifdef TIME
benchtime = time(0) - starttime - nulltime;
printf("Dhrystone time for %ld passes = %ld\n", (long) LOOPS, benchtime);
printf("This machine benchmarks at %ld dhrystones/second\n",
   ((long) LOOPS) / benchtime);
#endif
#ifdef TIMES
   times(&tms);
   benchtime = tms.tms_utime - starttime - nulltime;
printf("Dhrystone time for %ld passes = %ld\n", (long) LOOPS, benchtime/HZ);
printf("This machine benchmarks at %ld dhrystones/second\n",
   ((long) LOOPS) * HZ / benchtime);
#endif

}

Proc1(PtrParIn)
REG RecordPtr   PtrParIn;
{
#define   NextRecord   (*(PtrParIn->PtrComp))

   structassign(NextRecord, *PtrGlb);
   PtrParIn->IntComp = 5;
   NextRecord.IntComp = PtrParIn->IntComp;
   NextRecord.PtrComp = PtrParIn->PtrComp;
   Proc3(NextRecord.PtrComp);
   if (NextRecord.Discr == Ident1)
   {
      NextRecord.IntComp = 6;
      Proc6(PtrParIn->EnumComp, &NextRecord.EnumComp);
      NextRecord.PtrComp = PtrGlb->PtrComp;
      Proc7(NextRecord.IntComp, 10, &NextRecord.IntComp);
   }
   else
      structassign(*PtrParIn, NextRecord);

#undef   NextRecord
}

Proc2(IntParIO)
OneToFifty   *IntParIO;
{
   REG OneToFifty      IntLoc;
   REG Enumeration      EnumLoc;

   IntLoc = *IntParIO + 10;
   for(;;)
   {
      if (Char1Glob == 'A')
      {
         --IntLoc;
         *IntParIO = IntLoc - IntGlob;
         EnumLoc = Ident1;
      }
      if (EnumLoc == Ident1)
         break;
   }
}

Proc3(PtrParOut)
RecordPtr   *PtrParOut;
{
   if (PtrGlb != NULL)
      *PtrParOut = PtrGlb->PtrComp;
   else
      IntGlob = 100;
   Proc7(10, IntGlob, &PtrGlb->IntComp);
}

Proc4()
{
   REG boolean   BoolLoc;

   BoolLoc = Char1Glob == 'A';
   BoolLoc |= BoolGlob;
   Char2Glob = 'B';
}

Proc5()
{
   Char1Glob = 'A';
   BoolGlob = FALSE;
}

extern boolean Func3();

Proc6(EnumParIn, EnumParOut)
REG Enumeration   EnumParIn;
REG Enumeration   *EnumParOut;
{
   *EnumParOut = EnumParIn;
   if (! Func3(EnumParIn) )
      *EnumParOut = Ident4;
   switch (EnumParIn)
   {
   case Ident1:   *EnumParOut = Ident1; break;
   case Ident2:   if (IntGlob > 100) *EnumParOut = Ident1;
         else *EnumParOut = Ident4;
         break;
   case Ident3:   *EnumParOut = Ident2; break;
   case Ident4:   break;
   case Ident5:   *EnumParOut = Ident3;
   }
}

Proc7(IntParI1, IntParI2, IntParOut)
OneToFifty   IntParI1;
OneToFifty   IntParI2;
OneToFifty   *IntParOut;
{
   REG OneToFifty   IntLoc;

   IntLoc = IntParI1 + 2;
   *IntParOut = IntParI2 + IntLoc;
}

Proc8(Array1Par, Array2Par, IntParI1, IntParI2)
Array1Dim   Array1Par;
Array2Dim   Array2Par;
OneToFifty   IntParI1;
OneToFifty   IntParI2;
{
   REG OneToFifty   IntLoc;
   REG OneToFifty   IntIndex;

   IntLoc = IntParI1 + 5;
   Array1Par[IntLoc] = IntParI2;
   Array1Par[IntLoc+1] = Array1Par[IntLoc];
   Array1Par[IntLoc+30] = IntLoc;
   for (IntIndex = IntLoc; IntIndex <= (IntLoc+1); ++IntIndex)
      Array2Par[IntLoc][IntIndex] = IntLoc;
   ++Array2Par[IntLoc][IntLoc-1];
   Array2Par[IntLoc+20][IntLoc] = Array1Par[IntLoc];
   IntGlob = 5;
}

Enumeration Func1(CharPar1, CharPar2)
CapitalLetter   CharPar1;
CapitalLetter   CharPar2;
{
   REG CapitalLetter   CharLoc1;
   REG CapitalLetter   CharLoc2;

   CharLoc1 = CharPar1;
   CharLoc2 = CharLoc1;
   if (CharLoc2 != CharPar2)
      return (Ident1);
   else
      return (Ident2);
}

boolean Func2(StrParI1, StrParI2)
String30   StrParI1;
String30   StrParI2;
{
   REG OneToThirty      IntLoc;
   REG CapitalLetter   CharLoc;

   IntLoc = 1;
   while (IntLoc <= 1)
      if (Func1(StrParI1[IntLoc], StrParI2[IntLoc+1]) == Ident1)
      {
         CharLoc = 'A';
         ++IntLoc;
      }
   if (CharLoc >= 'W' && CharLoc <= 'Z')
      IntLoc = 7;
   if (CharLoc == 'X')
      return(TRUE);
   else
   {
      if (strcmp(StrParI1, StrParI2) > 0)
      {
         IntLoc += 7;
         return (TRUE);
      }
      else
         return (FALSE);
   }
}

boolean Func3(EnumParIn)
REG Enumeration   EnumParIn;
{
   REG Enumeration   EnumLoc;

   EnumLoc = EnumParIn;
   if (EnumLoc == Ident3) return (TRUE);
   return (FALSE);
}

#ifdef   NOSTRUCTASSIGN
memcpy(d, s, l)
register char   *d;
register char   *s;
int   l;
{
   while (l--) *d++ = *s++;
}
#endif

/*
 *   Library function for compilers with no time(2) function in the
 *   library.
 */
#ifdef   NOTIME
long   time(p)
long   *p;
{      /* CI-C86 time function - don't use around midnight */
   long   t;
   struct regval {unsigned int ax,bx,cx,dx,si,di,ds,es; } regs;

   regs.ax = 0x2c00;
   sysint21(&regs, &regs);
   t = ((regs.cx>>8)*60L + (regs.cx & 0xff))*60L + (regs.dx>>8);
   if (p) *p = t;
   return t;
}
#endif
Bruce Abbott is offline  
Old 21 September 2023, 08:18   #2
Olaf Barthel
Registered User
 
Join Date: Aug 2010
Location: Germany
Posts: 532
Quote:
Originally Posted by Bruce Abbott View Post
I'm trying to compile a "DHRYSTONE" benchmark program written in 1984 that I found on Fish disk #378. When I turn register parameters on SASC says it can't do that because there are no 'prototypes' for the functions involved.

That sort of makes sense, so I use the 'GenProto' compile option to create a file that looks like this:-
The functions for which the compiler generated the prototypes use the "boolean" type as a return value. At the point where you include the prototype header file, that type is not yet defined. Hence the hickups which follow later: if the type is undefined, the compiler will default to an "int" type and, finding that the actual implementation uses a different type, will invariably produce the kind of warnings and error messages which make 'C' such a beloved double-edged razorblade
Olaf Barthel is offline  
Old 22 September 2023, 05:47   #3
Bruce Abbott
Registered User
 
Bruce Abbott's Avatar
 
Join Date: Mar 2018
Location: Hastings, New Zealand
Posts: 2,584
Thanks for that. After posting I thought about it and figured that was probably the reason - and it was! It also had problems with strcmp and strcpy which I fixed by writing prototypes for them. Wonder why SASC didn't pick them up?

I then tried to figure out what the code was actually doing, as the description is vague and there are no comments in the actual code. I didn't know anything about Dhrystone except that it is an 'integer' benchmark. Turns out it does a bunch of things including string copy and compare, a 5 line switch block, and 1 and 2 dimensional array manipulations, as well as some very simple integer and boolean math.

Wikipedia says that Dhrystone is 'susceptible to compiler optimizations', and it is correct. Compiling under SACS 6.58 with register variables and optimized for speed, the code executed 3.5 times faster than the register based executable included in the archive! That's a huge difference purely due to the compiler. I can imagine Amiga users being sorely disappointed by how 'slow' their machine was according to this benchmark, when it was actually the compiler that was holding it back!
Bruce Abbott is offline  
Old 23 September 2023, 11:51   #4
Olaf Barthel
Registered User
 
Join Date: Aug 2010
Location: Germany
Posts: 532
Quote:
Originally Posted by Bruce Abbott View Post
Thanks for that. After posting I thought about it and figured that was probably the reason - and it was! It also had problems with strcmp and strcpy which I fixed by writing prototypes for them. Wonder why SASC didn't pick them up?
Because the prototypes you provided are merely "hints", not what the prototype generation process performed by the compiler accomplishes: it looks for implementations, then supplies the prototypes.

Quote:
I then tried to figure out what the code was actually doing, as the description is vague and there are no comments in the actual code.
Which is how these things were done back in the day

This benchmark has been around since the early 1980'ies in several flavours, if memory serves. Hence, the manner in which it stirs the soup (clockwise or counterclockwise) may differ between platform/implementation languages with the original author perhaps being less than comfortable with the 'C' environment, compared to Pascal or FORTRAN.

In the early 1980'ies the state of the art of the 'C' language documentation was still the first edition "The 'C' programming language", which did not even document the full runtime library. You were ahead of the pack if you were familiar with the Unix programming environment (documented in a separate book by half of the 'C' team).

Quote:
I didn't know anything about Dhrystone except that it is an 'integer' benchmark. Turns out it does a bunch of things including string copy and compare, a 5 line switch block, and 1 and 2 dimensional array manipulations, as well as some very simple integer and boolean math.

Wikipedia says that Dhrystone is 'susceptible to compiler optimizations', and it is correct. Compiling under SACS 6.58 with register variables and optimized for speed, the code executed 3.5 times faster than the register based executable included in the archive! That's a huge difference purely due to the compiler. I can imagine Amiga users being sorely disappointed by how 'slow' their machine was according to this benchmark, when it was actually the compiler that was holding it back!
You can't hold this benchmark to modern standards, both given its age and how the definition of this kind of performance measurement has changed over the decades. Part of the problem was the intent to render different machines, using different implementations of the benchmark, comparable. That's not exactly like comparing apples and oranges, more like comparing apples and four-dimensional russet walrusses

Enabling 'C' compiler optimizations is cheating. So is enabling FORTRAN compiler optimizations. So is running the benchmark on a machine which features code and/or data caches. So is running the benchmark on a machine which uses a different instruction set architecture, pipelining, memory hierarchy, ABI, etc.
Olaf Barthel is offline  
Old 26 September 2023, 15:02   #5
Bruce Abbott
Registered User
 
Bruce Abbott's Avatar
 
Join Date: Mar 2018
Location: Hastings, New Zealand
Posts: 2,584
Quote:
Originally Posted by Olaf Barthel View Post
Enabling 'C' compiler optimizations is cheating. So is enabling FORTRAN compiler optimizations. So is running the benchmark on a machine which features code and/or data caches. So is running the benchmark on a machine which uses a different instruction set architecture, pipelining, memory hierarchy, ABI, etc.
OK so I recompiled with no optimizations and disabled CPU caches and now it's only 2.7 times faster.
Bruce Abbott is offline  
Old 26 September 2023, 21:18   #6
alkis
Registered User
 
Join Date: Dec 2010
Location: Athens/Greece
Age: 53
Posts: 719
Quote:
Originally Posted by Bruce Abbott View Post
... It also had problems with strcmp and strcpy which I fixed by writing prototypes for them. Wonder why SASC didn't pick them up?
I think for those you have to #include <string.h>
alkis is offline  
 


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools

Similar Threads
Thread Thread Starter Forum Replies Last Post
SASC - Hello inutition world? elwis Coders. General 6 28 January 2018 20:15
Sasc swampcretin request.Apps 2 29 May 2005 14:52

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +2. The time now is 07:18.

Top

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.
Page generated in 0.07035 seconds with 13 queries