User avatar
DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

StdLib Dirty Replacement Functions:

Wed Jul 24, 2019 8:51 pm

There are times when it is prefferable to not use the C Standard Library functions, such as when working with BareMetal and not wanting to include a C Standard Library. Many of the functions that we use are simple things that are fairly easy to quickly implement in a crude way in C, though there remains the question of how to implement them (and when what way is best).

As such I am presenting some quick and crude implementations of a few of these functions , and allowing the thread to lead to more knowledge that may be of aid to all of us. No matter how long we have been coding we can always stand to learn more. Also note that many of these are quickly written for this thread, and not given a lot of testing (just enough to see that they work).

NOTE0: I am using unsigned long in place of size_t in these examples. I made this choice as that is the most likely case if using in bare metal.

NOTE1: When replacing small standard library functions, it is prefereble to make the names of your implementation different from the standard, in case the compiler attempts to use some tricks and inline a builtin version of the standard library function. As such I use cammel case function names for this (taking advantage of the fact that C is case sensitive).

NOTE2: Other small functions you often write replacements for that are part of the standard C library are welcome to this thread as well, if you wish.


char *strcpy(char *dst, char *src);
This is a very simple one with a lot of possibilities, depending on the usage.

One common, and very crude, implementation would be:

Code: Select all

char *StrCpy(char *dst, char *src)
{
  for (long cnt=0; (dst[cnt]=src[cnt]; cnt++);
  return dst;
}
//
The above does what it is supposed to, and quite well at that.

A small improvement would be to implement the above as a macro:

Code: Select all

#define StrCpy(dst,src) (for (long cnt=0; dst[cnt]=src[cnt]; cnt++), dst)
//
This will be a bit more effecient, avoiding the overhead of a function call.

We can also do the same with pointers:

Code: Select all

#define StrCpy(dst,src) ({char *t0, *t1; t0=dst; \
                           t1=src; while(*t0++=*t1++);}, t0)
//
This is begining to get a bit difficult to read though.

Then there is the use of a simple assembly language bytewise copy:

Code: Select all

  EXPORT StrCpy

StrCpy
  MOV   R2,R0
StrCpyLp
  LDRB  R3,[R1], #1
  ORRS  R3,R3,R3
  STRB  R3,[R2], #1
  BNE   StrCpyLp
  MOV   R15,R14
  
  END
Prety simple, so much so that comments are not needed (see assembly can be simple enough to be self commenting :) ).


Now there are cases where we know how much we want in the destination, and it does not matter if we copy a little bit of garbage after the nul byte terminator. For small cases we can really optimize things. For example for 8 bytes destination (common case) we could:

Code: Select all

inline char *StrCpy8(char *dst, char *src)
{
__asm__( "LDMEA R1,{R2,R3} \n"
         "STMEA R0,{R2,R3} \n")
}
//
Or similar for the common case of 12-byte strings:

Code: Select all

inline char *StrCpy12(char *dst, char *src)
{
__asm__( "LDMEA R1,{R2,R3} \n"
         "STMEA R0,{R2,R3} \n"
         "LDR   R2, [R1,#8] \n"
         "STR   R2, [R0,#8] \n")
}
//
Of course these could be made better by making them a macro. Note that I only used safe registers in that.


void *memcpy(void *dst, void *src, unsigned long cnt);
For largish blocks of memory, using only the ARM CPU we could use (assumes memory blocks are Word Aligned [32-bit word on ARM]):

Code: Select all

  EXPORT MemCpy

MemCpy
  STMFD R13!,{R4-R7}

  MOV   R3, R0

  TST   R2, #&FFFFFFF0        ;16 Bytes at a time to copy.
lp0
  LDMEANE R1!,{R4-R7}
  SUBNE R2, R2, #16
  STMEANE R3!,{R4-R7}
  TSTNE R2, #&FFFFFFF0
  BNE   lp0

  TST   R2,#&FFFFFFFC         ;4 Bytes at a time to copy.
lp1
  LDRNE R4, [R1], #4
  SUBNE R2, R2, #4
  STRNE R4, [R3], #4
  TSTNE R2,#&FFFFFFFC
  BNE   lp1

  CMP   R2,#0
lp2
  LDRBNE R4,[R1], #1           ;Single byte at a time.
  SUBSNE R2,R2,#1
  STRBNE R4,[R3], #1
  BNE   Lp2
  
  LDMFD R13!,{R4-R7}
  MOV   R15,R14
  
  END
With this one there are some obvious possible improvements that could be made. Though it is a decent example, and extremely simple.

Obviously it would be more effecient to use NEON (or just VFP if ARMv6). And even better would be to use DMA (then could interleve other code while waiting for the DMA operation to complete).

For small blocks of memory, so long as the block is 32-bit word alligned (should be safe assumption in most cases):

Code: Select all

  EXPORT MemCpy

MemCpy
  MOV   R3, R0
lp0
  CMP   R1, #0
  LDRGE R4, [R1], #4
  SUBSGE R1, #4
  STRGE R4, [R1], #4
  BGE   lp0
  
  MOV   R15,R14
I do not do this particular one in C, just in Assembly that can be called from C. So I am not providing a C version of MemCpy().

int strcmp(char *src0, char *src2);
This one is usually not worth optimizing to much, just a quick implementation in C gives us:

Code: Select all

int StrCmp(char *src0, char *src1)
{
  int cnt;
  for (cnt = 0; (src0[cnt] == src1[cnt]) && src0[cnt] && scr1[cnt]; cnt++);
  return src0[cnt]-src1[cnt];
}
//
This is one of those functions that does not really provide a good excuse to optimize at all, at least not in most uses I have had for it (a few exceptions, like a compilers tokenizer).



Thank you for getting me back going. The inspiration of this thread
In another thread I had posted some code that included a crude implementation of strcpy(), and was getting some good feedback about the implementation, I was enjoying finally getting positive critisism again on these forums (it still does exist on these forums :) ), this is how people learn. Unfortunately that thread got locked do to other not as positive statements (that is ok, at least now I know that the positive is still here).

So I AM BACK :) . If I can actually help, and learn in the process I am happy to post some thoughts, and code concepts :) .

I am happy to recieve positive critisism (that is what created this thread). I do ask to keep it clean and constructive, please.

I very much enjoy the feedback and forth as I still learn every day from such things. I enjoy people criticizing my code when the see a better way, that is how we learn (and the reason I am back is a few people are doing this again).
Last edited by DavidS on Wed Jul 24, 2019 9:02 pm, edited 1 time in total.
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

User avatar
DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: StdLib Dirty Replacement Functions:

Wed Jul 24, 2019 8:58 pm

The first piece of positive criticism from the thread that inspired this one (there are a few more, though it takes a few minutes to copy from a locked thread):
Michiel O. wrote:
DavidS wrote: There are plenty of other things that people could criticize about that code.

First and foremost, I was surprised about your implementation of strcpy, since I heard you praising the benefits of pointers on multiple occasions. Yet, you use an extra index variable and array notation:

Code: Select all


char *StrCpyDavid(char *s, char *t){
    for(int cnt = 0; (s[cnt] = t[cnt]); cnt++);
    return s;
    }
My suggestion is to use pointers for strcpy:

Code: Select all

char *StrCpyMichiel(char *dst, char *src) {
    char *d = dst;
    while (*dst++ = *src++);
    return d;
    }
Not only is this more in the spirit of C, but it's also quite a lot more efficient:

$ cc strcpy.c && ./a.out # timed on a RPi3B
David took 11287 microseconds
Michiel took 7603 microseconds

Furthermore, I find ARM assembly beautiful. It has a blend of nostalgia and esthetic pleasure for me. Nostalgia, because the 6502 was the first CPU I learned to program in machine language on, and that CPU leaned towards RISC, in contrast to the Z80). Later the 68000 family came along in the same flavour. And that all culminated in the ARM instruction set, which is nicely orthogonal, unlike the CISC x86/amd64 architecture.
Interesting indeed.

Thank you a thread late for that again.
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

jahboater
Posts: 4674
Joined: Wed Feb 04, 2015 6:38 pm

Re: StdLib Dirty Replacement Functions:

Fri Jul 26, 2019 6:53 am

This might be helpful. The "inline" keyword is just a hint to the compiler.
You can change it to force inlining with:-

Code: Select all

#define inline    __inline__ __attribute__ ((always_inline))
#define noinline  __attribute__ ((noinline))
This define stops the compiler moving asm() statements around or even deleting them.

Code: Select all

#define asm __asm__ __volatile__
DavidS wrote:
Wed Jul 24, 2019 8:51 pm
Now there are cases where we know how much we want in the destination, and it does not matter if we copy a little bit of garbage after the nul byte terminator. For small cases we can really optimize things. For example for 8 bytes destination (common case) we could:

Code: Select all

inline char *StrCpy8(char *dst, char *src)
{
__asm__( "LDMEA R1,{R2,R3} \n"
         "STMEA R0,{R2,R3} \n")
}
//
Are LDRD and STRD fussy about alignment?
For:
char buf[20];
memcpy( buf, str, 8 );

the compiler does:-

ldr r2, [r3] @ unaligned
ldr r3, [r3, #4] @ unaligned
strd r2, [sp, #4]

The big problem in StrCpy8() is that you are using R0 and R1 presumably because that's where the ABI puts function call arguments.
But you have also given "inline". In that case there is no function call and therefore "dst" and "src" may be in any register.
Also of course its no longer safe to alter R2 and R3 without telling the compiler.

You can either use "noinline" to make sure its always a function, or use extended asm which is always best (its safe and much faster) - even if you don't like it :(
Basic asm (without any ':') might take the safe option and save/restore all the registers.

swampdog
Posts: 238
Joined: Fri Dec 04, 2015 11:22 am

Re: StdLib Dirty Replacement Functions:

Fri Jul 26, 2019 8:49 am

The trouble these days with trying to beat the compiler is they're pretty clever now. Take your StrCpy(), slightly modified old-school..

Code: Select all

char*
StrCpy(register char *d, register const char *s)
{
 register char *r=d;
 while (*d++ = *s++);
 return r;
}
(gcc -c sc.c && objdump -d sc.o)

The above isn't supposed to make any difference over the version without "register" keyword but it does and the addition of "const" makes no difference at all but doesn't mean it can't.

The above is moot though. Pass "-O" flag and compiler produces much more efficient code which, for this example at least, is the same regardless of "register/const" being there or not.

jahboater
Posts: 4674
Joined: Wed Feb 04, 2015 6:38 pm

Re: StdLib Dirty Replacement Functions:

Fri Jul 26, 2019 9:42 am

swampdog wrote:
Fri Jul 26, 2019 8:49 am
Pass "-O" flag and compiler produces much more efficient code which, for this example at least, is the same regardless of "register/const" being there or not.
The "register" keyword is only useful nowadays to force a variable into a specific register.
For example:

register int num asm("r5");

forces the variable "num" to be held in R5.

Extremely unwise of course, except perhaps for setting up inline system calls.

User avatar
DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: StdLib Dirty Replacement Functions:

Fri Jul 26, 2019 4:25 pm

jahboater wrote:
Fri Jul 26, 2019 6:53 am
This might be helpful. The "inline" keyword is just a hint to the compiler.
You can change it to force inlining with:-

Code: Select all

#define inline    __inline__ __attribute__ ((always_inline))
#define noinline  __attribute__ ((noinline))
This define stops the compiler moving asm() statements around or even deleting them.

Code: Select all

#define asm __asm__ __volatile__
Thank you for that.
DavidS wrote:
Wed Jul 24, 2019 8:51 pm
Now there are cases where we know how much we want in the destination, and it does not matter if we copy a little bit of garbage after the nul byte terminator. For small cases we can really optimize things. For example for 8 bytes destination (common case) we could:

Code: Select all

inline char *StrCpy8(char *dst, char *src)
{
__asm__( "LDMEA R1,{R2,R3} \n"
         "STMEA R0,{R2,R3} \n")
}
//
Are LDRD and STRD fussy about alignment?
For speed more so than LDM/STM. Have recently done a lot of testing on that.
For:
char buf[20];
memcpy( buf, str, 8 );

the compiler does:-

ldr r2, [r3] @ unaligned
ldr r3, [r3, #4] @ unaligned
strd r2, [sp, #4]

The big problem in StrCpy8() is that you are using R0 and R1 presumably because that's where the ABI puts function call arguments.
But you have also given "inline". In that case there is no function call and therefore "dst" and "src" may be in any register.
Also of course its no longer safe to alter R2 and R3 without telling the compiler.

You can either use "noinline" to make sure its always a function, or use extended asm which is always best (its safe and much faster) - even if you don't like it :(
Basic asm (without any ':') might take the safe option and save/restore all the registers.
You are correct, my error on that one. I should have stacked R2 and R3. I did not know that the compiler would break the rules of parameter passing when inlining though, interesting, thank you for that.
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

User avatar
DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: StdLib Dirty Replacement Functions:

Fri Jul 26, 2019 4:28 pm

jahboater wrote:
Fri Jul 26, 2019 9:42 am
swampdog wrote:
Fri Jul 26, 2019 8:49 am
Pass "-O" flag and compiler produces much more efficient code which, for this example at least, is the same regardless of "register/const" being there or not.
The "register" keyword is only useful nowadays to force a variable into a specific register.
For example:

register int num asm("r5");

forces the variable "num" to be held in R5.

Extremely unwise of course, except perhaps for setting up inline system calls.
+1. In 99% of cases where one may think to use it it will not help.
Though it can on occasion be useful for local variables when you are going to be making a specific series of system calls, at least for some OS's (and likely some cases for bare metal projects).
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

User avatar
DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: StdLib Dirty Replacement Functions:

Fri Jul 26, 2019 4:31 pm

OT:
Sorry about the one extra day delay in responding. I am not familiar enough with Linux, and after an update I was having trouble with the KMS VideoCore IV GL driver on a composite display, so fought it for most of yesterday (ended up just hooking up an HDMI display that does not have sound [and as I need speech synth that requires having an extra RPi running).

Though back on track there.

thank you for the very positive feedback.
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

jahboater
Posts: 4674
Joined: Wed Feb 04, 2015 6:38 pm

Re: StdLib Dirty Replacement Functions:

Fri Jul 26, 2019 5:00 pm

DavidS wrote:
Fri Jul 26, 2019 4:25 pm
I should have stacked R2 and R3.
Just tell the compiler that you have modified R2 and R3 and it will avoid using them. Saving them on the stack and restoring them is obviously costly since memory access is very slow.
DavidS wrote:
Fri Jul 26, 2019 4:25 pm
I did not know that the compiler would break the rules of parameter passing when inlining though, interesting, thank you for that.
Its no longer a function call, so its not breaking any rules. The code is merged in with that of the caller.

Inlining is very important for modern compilers. Its more than just avoiding the function call overhead.
The ABI mandates that at the call site the parameters must be in certain registers. That adds a constraint for the register allocater/scheduler throughout the preceding code to ensure values are ready in the right registers.

With inlining, its much easier. There is no function call and the "inlined" code will just use the variables in whatever registers they happen to be in. Even for quite large functions, inlining can save code. GCC will always inline functions that are called once.
More interesting is partial inlining. Here the compiler, having knowledge of the actual parameters, may inline only those parts of the function that are needed!

User avatar
Paeryn
Posts: 2657
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: StdLib Dirty Replacement Functions:

Fri Jul 26, 2019 5:08 pm

DavidS wrote:
Fri Jul 26, 2019 4:25 pm

You are correct, my error on that one. I should have stacked R2 and R3. I did not know that the compiler would break the rules of parameter passing when inlining though, interesting, thank you for that.
The compiler isn't breaking any rules when inlining. If a function gets inlined then no function call happens and therefore no parameters are being passed, the function will appear as though it were written inside the callee.
She who travels light — forgot something.

jahboater
Posts: 4674
Joined: Wed Feb 04, 2015 6:38 pm

Re: StdLib Dirty Replacement Functions:

Fri Jul 26, 2019 5:09 pm

Thinking about your requirements - to avoid using the C library (quite reasonable for bare metal).

The functions StrCpy8() and StrCpy12() will always be faster if they just use memcpy().
The compiler has knowledge that your functions cannot have:-
- if the source argument is a literal StrCpy8( buf, "abc" ) then a simple load immediate/STR is all that's needed.
- if the source or destination have alignment requirements (or not) then the code can be adjusted as needed.
- some copies might be better done with 128-bit SIMD registers.
and so on ....

The big trouble is that while memcpy() will be converted to a single instruction or two in any optimized C compile, if you turn optimization off with -O0 then it will likely call the library function. Not what you want :(

Not sure what the answer is!
Last edited by jahboater on Fri Jul 26, 2019 5:24 pm, edited 1 time in total.

Heater
Posts: 13268
Joined: Tue Jul 17, 2012 3:02 pm

Re: StdLib Dirty Replacement Functions:

Fri Jul 26, 2019 5:23 pm

Call me crazy but I can't help thinking that if one is going to make replacement functions for the C standard libraries normally supplied by ones compiler, then they had better have the same name and parameters as the standard ones. Same signature. Otherwise they are not replacements but additions.

Typically when I have done this it is to make it possible to run the same application code on a bare metal system of limited resources as I do on a PC or where ever, without having to change the application's source. This is convenient and useful for making unit tests on the PC.

One can ensure that the compiler is not pulling in it's own library versions by using a compiler option "nostdlib" or whatever it's called.

The last time I played with this sort of thing I found that GCC had "weak linkage" for these things, so that if your code contained a function with the same name as a standard lib function, that would get used in preference.

User avatar
DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: StdLib Dirty Replacement Functions:

Fri Jul 26, 2019 5:26 pm

jahboater wrote:
Fri Jul 26, 2019 5:09 pm
Thinking about your requirements - to avoid using the C library (quite reasonable for bare metal).

The functions StrCpy8() and StrCpy12() will always be faster if just using memcpy().
The compiler has knowledge that your functions cannot have:-
- if the source argument is a literal StrCpy8( buf, "abc" ) then a simple load immediate/STR is all that's needed.
- if the source or destination have alignment requirements (or not) then the code can be adjusted as needed.
- some copies might be better done with 128-bit SIMD registers.
and so on ....

The big trouble is that while memcpy() will be converted to a single instruction or two in any optimized C compile, if you turn optimization off with -O0 then it will likely call the library function. Not what you want :(

Not sure what the answer is!
Or different compilers may have different ways of accomplishing this making for different cases of what is best.

It is headaches like that which keep me using Assembly language for many things.

Though C is a good language, one of the most reasonable and easiest to use High Level Languages in my personal view. And it is sometimes needed to work in C even when coding at the level below an OS (such as a project specifically requiring C as the implementation language).
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

jahboater
Posts: 4674
Joined: Wed Feb 04, 2015 6:38 pm

Re: StdLib Dirty Replacement Functions:

Fri Jul 26, 2019 5:54 pm

DavidS wrote:
Fri Jul 26, 2019 5:26 pm
Or different compilers may have different ways of accomplishing this making for different cases of what is best.

It is headaches like that which keep me using Assembly language for many things.

Though C is a good language, one of the most reasonable and easiest to use High Level Languages in my personal view. And it is sometimes needed to work in C even when coding at the level below an OS (such as a project specifically requiring C as the implementation language).
I think what you are doing is very advanced and difficult to get right.

Mixing C and assembler for truly high speed code, involves understanding how modern compilers do things, how you can work with the compiler, how you can best write the inline assembler to exploit the compiler, and how you can best write the inline assembler to allow the compiler to exploit what you have done.

Very difficult :(

The golden rules with the popular compilers (GCC, Clang, ICC say) are 1) tell the compiler everything (exactly) and 2) let the compiler do as much as possible.

Thanks for the interesting thread!

User avatar
DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: StdLib Dirty Replacement Functions:

Fri Jul 26, 2019 6:03 pm

jahboater wrote:
Fri Jul 26, 2019 5:54 pm
I think what you are doing is very advanced and difficult to get right.

Mixing C and assembler for truly high speed code, involves understanding how modern compilers do things, how you can work with the compiler, how you can best write the inline assembler to exploit the compiler, and how you can best write the inline assembler to allow the compiler to exploit what you have done.

Very difficult :(
Agreed on that. Especially as there are hundreds of currently mainained C compilers, not all following the same rules in optimizing. It is nearly impossible to get it correct for all compilers on any target.
The golden rules with the popular compilers (GCC, Clang, ICC say) are 1) tell the compiler everything (exactly) and 2) let the compiler do as much as possible.
While I agree very much if you are using only one of the big ones (did you mean to say LCC?, not heard of ICC), not so much when attempting to allow for any compiler.

I wish there were universal rules.

Though for those using Norcroft C, GCC, or CLang please follow the advice given in what jahboater wrote.

Thank you for that.
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

jahboater
Posts: 4674
Joined: Wed Feb 04, 2015 6:38 pm

Re: StdLib Dirty Replacement Functions:

Fri Jul 26, 2019 6:13 pm

DavidS wrote:
Fri Jul 26, 2019 6:03 pm
While I agree very much if you are using only one of the big ones (did you mean to say LCC?, not heard of ICC), not so much when attempting to allow for any compiler.
ICC is Intel's C compiler which has always been highly regarded. I believe ICC and GCC work together in that they understand each others extensions (and obviously asm syntax). Sadly you have to pay for it.

The other big compiler of course is armcc, but that is based on Clang/LLVM which I mentioned above.

For me, GCC is the gold standard. And happily its available on all the platforms I would want to program on.

Again, for me, if a compiler doesn't fully support the latest standards (C++17, Fortran 2018, C18, D 2 etc) I will not consider using it. Thankfully GCC does - and emits code that's faster than I can write in assembler :)

User avatar
DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: StdLib Dirty Replacement Functions:

Fri Jul 26, 2019 9:41 pm

jahboater wrote:
Fri Jul 26, 2019 6:13 pm
DavidS wrote:
Fri Jul 26, 2019 6:03 pm
While I agree very much if you are using only one of the big ones (did you mean to say LCC?, not heard of ICC), not so much when attempting to allow for any compiler.
ICC is Intel's C compiler which has always been highly regarded. I believe ICC and GCC work together in that they understand each others extensions (and obviously asm syntax). Sadly you have to pay for it.
I did not know that Intel C (which is what I had always known it as) supported the ARM host and targert. I will have to look into that.
The other big compiler of course is armcc, but that is based on Clang/LLVM which I mentioned above.
Ok, I thought that Norcroft C was bigger than armcc, Norcroft C has been in use longer. So I learn something new, thank you.
For me, GCC is the gold standard. And happily its available on all the platforms I would want to program on.

Again, for me, if a compiler doesn't fully support the latest standards (C++17, Fortran 2018, C18, D 2 etc) I will not consider using it. Thankfully GCC does - and emits code that's faster than I can write in assembler :)
Oh I missed one, C18. Is C18 at least worthwhile?

Though you do mention a significant difference between a C Compiler and a Compiler Suite. GCC (GNU Compiler Collection) is a Compiler Suite, while Norcroft C is a C compiler. GCC supports many languages, Norcroft C supports C.

Call me a bit old fashioned, though I prefer compilers that focus on a single language, and better optimizing for that language. This is my personal preference. I use GCC quite a bit, though I am not really a huge fan (as even with -O0 it takes a while to compile compared to language specific compilers, and that is part of the run time [many things end up compiled many times over], and power consumption of the program being compiled).

I could care less about the C++ newer standards, they added way to much useless stuff for me long ago.
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

Heater
Posts: 13268
Joined: Tue Jul 17, 2012 3:02 pm

Re: StdLib Dirty Replacement Functions:

Sat Jul 27, 2019 5:32 am

DavidS,
Though you do mention a significant difference between a C Compiler and a Compiler Suite. GCC (GNU Compiler Collection) is a Compiler Suite, while Norcroft C is a C compiler. GCC supports many languages, Norcroft C supports C.
That is not what the guys who created Norcroft say about it:

"The Norcroft compiler suite; technical overview and details 1993
The Norcroft compiler suite is a portable (across host and target systems) set of compilers for a variety of languages. It consists of a set of interchangeable (language dependent) front-ends which interface to a common intermediate code generator and optimiser (the `middle'-end) and a set of back-ends for most common RISC and CISC architectures. The interface to the back-end is clean and narrow which means that implementing the compilers for a new architecture is relatively fast. Re-hosting them is also easy - the compilers have been written to be insensitive to byte ordering and floating point representation of the host on which they are run, so cross compiling from one machine to another is possible, either as a step in porting the code to a new target or in support of embedded processors."


http://www.codemist.co.uk/ncc/index.html

Codemist clearly set out to (and did) create many compilers for many different languages and architectures, so naturally they organized their design as multiple front ends, multiple back ends and as much as possible common in the middle. That same overall strategy as GCC.

jahboater
Posts: 4674
Joined: Wed Feb 04, 2015 6:38 pm

Re: StdLib Dirty Replacement Functions:

Sat Jul 27, 2019 6:18 am

DavidS wrote:
Fri Jul 26, 2019 9:41 pm
I did not know that Intel C (which is what I had always known it as) supported the ARM host and targert.
I don't think it does , though Intel did have xscale. I just mentioned it because it is a popular compiler. You have to pay for it, so I'll never use it!
Oh I missed one, C18. Is C18 at least worthwhile?
Its mostly bug fixes, the last version with lots of new language features was C11.
GCC has experimental support for C2x which is the next 10 year cycle version.
DavidS wrote:
Fri Jul 26, 2019 9:41 pm
Call me a bit old fashioned, though I prefer compilers that focus on a single language, and better optimizing for that language.
Are you suggesting that Norcroft C produces better code than GCC 9.1 ?
If so, I might take a look at it. Looks like they (Codemist) have ceased trading though.

As for compile time, yes, GCC is a huge program. But the languages it supports are very big languages (imagine writing a compiler for C++17 - the mind boggles). The new front end for D added a million lines of code. And it supports lots of target architectures.
GCC does compile pretty quickly on a Pi4 by the way. 54 million lines of code in 3 hours 15 mins doesn't seem too bad.

You can build GCC for a specific language, I have never tried it, but I think you can just do "--enable-languages=c" and get a C only compiler that should be a lot smaller.

swampdog
Posts: 238
Joined: Fri Dec 04, 2015 11:22 am

Re: StdLib Dirty Replacement Functions:

Tue Jul 30, 2019 1:07 pm

@jahboater
You can build GCC for a specific language, I have never tried it, but I think you can just do "--enable-languages=c" and get a C only compiler that should be a lot smaller.
I typically use..

Code: Select all

--enable-languages=c,c++
..as it's rare I need anything else. For a fast build (mainly to see if there's going to be any obvious problems) add in..

Code: Select all

--enable-languages=c,c++ --disable-multilib --disable-bootstrap
..along with the other options. The latter is definitely not recommended for a "production" compiler but it will save a ton of time.

--disable-multilib : dont build libs for all architectures (useless on a pi atm)
--disable-bootstrap : dont do stage 2 and 3

jahboater
Posts: 4674
Joined: Wed Feb 04, 2015 6:38 pm

Re: StdLib Dirty Replacement Functions:

Tue Jul 30, 2019 1:24 pm

swampdog wrote:
Tue Jul 30, 2019 1:07 pm
..along with the other options. The latter is definitely not recommended for a "production" compiler but it will save a ton of time.
The full build with the bootstrap took 3 hours 15 minutes on my 4GB Pi4 (no throttling).
That was for --enable-languages=c,c++

make -j5 and no need to worry about swapping anymore !

Return to “C/C++”