And -- pow! My memory was gone.

Once you are at the stage of a 32-bit processor, then you are generally unlikely to do much better by specialising your library code per processor, or using assembly. It makes a much bigger difference for smaller targets.

The exception is if you are able to make use of particular odd instructions or capabilities that your processor has, but that the compiler cannot generate automatically.

Reply to
David Brown
Loading thread data ...

Those odd instructions are typically something you can use in math operations. Often a CPU will have math instructions that are useful, but don't align directly with C conventions, such as non-standard multiply/divide bit sizes. Also, assembly will give you direct access to carry and overflow flags, and other odd instructions, such as count-leading-zeroes.

A good C compiler will also provide ways to gain access to those, but may be at a price of having to write convoluted code, and frequently checking assembler output. At that point, it's easier just to write the assembly yourself.

I'm not advocating that the average user go write his own math lib in assembly, but for a vendor trying to sell a high-performance toolchain, hiring an expert to optimize the last cycle out of math code can be worth it.

Reply to
Arlet Ottens

Don't use 'new'. Put your objects on the stack and let subroutines use them through references as function arguments, e.g.:

static int foo(MyClass& c) { c.do_something(); }

int main() { int some_data; ... some code ... MyClass my_object(some_data); ... some code ... foo(my_object); ... some code ... }

If your classes don't call new and your functions never call new and if you don't use classes from other libraries then malloc() should not be linked in. That's theory though :-(

For this approach (and placement new, see

formatting link
malloc is linked in.

bye Andreas

--
Andreas H√ľnnebeck | email: acmh@gmx.de
----- privat ---- | www  : http://www.huennebeck-online.de
 Click to see the full signature
Reply to
Andreas Huennebeck

Sometimes the good C compiler will generate odd instructions automatically - in which case it is better to write the more "natural" C code, as this gives the compiler the best chance of generating even better code. You'll want to check the generated assembly, of course. However, although compilers have got better at this sort of thing, they are far from perfect. They are also hampered by the limitations of C. For example, there is no good way to write a "rol" or "ror" instruction in C, and few if any compilers would generate it from raw C.

I agree with you that writing the assembly by hand is sometimes the easiest and clearest way - "intrinsic" C function wrappers around odd instructions are okay for an instruction or two, but quickly become messy. A spot of inline assembly can be much clearer than convoluted pseudo-C functions.

It can be worth the effort - but not always. You (or the compiler vendor) also has to take into account things like maintenance, testability, and stability in the face of changes to the compiler. It is more important to be correct, and to be sure that your code stays correct, than to be small and fast.

There are also different balances, which makes things difficult - often you can write code that is small /and/ fast, but sometimes it's a choice of small /or/ fast. Then there is accuracy and conformance to standards

- do you write your libraries to conform exactly to the standards (for IEEE-754, this means special handling of NaNs, signed zeros, etc., which often mean a hardware FPU unit is of only limited use), or do you write your libraries to conform to what your customers actually need (embedded systems seldom need NaNs, etc.)?

Sometimes it /is/ worth writing your own maths code (though it is seldom worth doing it in assembly). For example, it is typically not hard to write a sine function that is smaller and much faster than the maths library sinf(), and yet which is accurate enough for your motor controller or other application.

Reply to
David Brown

formatting link

It is also possible to use static objects that get constructed before main() - this will not lead to any mallocs and will also allow absolute addressing modes which are more efficient on some processors. Of course, you then have to be sure of the ordering of construction of these static objects (or use a two-part constructor and init method).

Reply to
David Brown

formatting link

void * malloc(size_t n) { assert(false); return 0; }

is pretty small -- maybe I'll use that.

--
Tim Wescott
Wescott Design Services
 Click to see the full signature
Reply to
Tim Wescott

That's what I'm doing! Unless I'm creating something dynamically and I've forgotten it (the code is old, and getting resurrected) -- now I need to go over the code with a fine-toothed comb.

--
Tim Wescott
Wescott Design Services
 Click to see the full signature
Reply to
Tim Wescott

void * malloc(size_t n) { return 0; }

is smaller. "assert" can bring in all sorts of junk, depending on how it is defined, and what flags you have enabled - if it tries to print out a message on stderr, then this may be the cause of some of your problems.

Reply to
David Brown

I sometimes use a trivial malloc and no free(), Just a few lines of code that allocates from a configurable static array. That allows me to allocate at run-time, without worrying about heap fragmentation or non-deterministic behaviour.

--

John Devereux
Reply to
John Devereux

Let me rephrase that for you: "assert" can bring in all sorts of junk, if you're dumb enough not to write your own. Honest libraries that are really intended for deep embedded use don't even _have_ an assert written, because they know that each development team is going to want its own (or is going to be too slow to use it). The rest need their assert function redefined.

So you spelunk through the headers and find out what the 'assert()' macro points to, and you write your own. This one's teeny, and if your debugger lets you set a variable you can break out and see what code was running in the previous function*. Sometimes you can even break out of the assert and correct the problem in the calling function that once, check to see that it's doing the right thing, then go back and find out why the assert was popping off:

static volatile int break_out = 0; void __assert_func(const char * this, int that, const char * the, const char * other) { while (break_out == 0) { } }

I really do know what I'm doing, even if I'm not as familiar with gnu tools and newlib as I'd like to be (and am becoming).

  • Which doesn't seem to be necessary with gdb, as it seems to do a good to superlative job of navigating the call stack, but it is a boon with other debuggers.
--
Tim Wescott
Wescott Design Services
 Click to see the full signature
Reply to
Tim Wescott

Me too. Or if I can't link because of the presence of free I'll put the assert there*, as a gentle hint to future developers that it's the wrong damn thing to do, and as a tool to figure out if it's actually being called in active code, or if it's buried somewhere in C++'s exit code (which should also never get called).

--
Tim Wescott
Wescott Design Services
 Click to see the full signature
Reply to
Tim Wescott

I'd start with "objdump --disassemble --reloc" or "nm".

Stefan

Reply to
Stefan Reuther

I have your book, though I haven't had time to read it yet, so I know you know your stuff. But even experts make mistakes on unfamiliar territory. And since you have been having issues with the libraries already, it's possible that you've been unintentionally been getting extra library code sneaking in here.

Reply to
David Brown

I had some private correspondence with Tim, and I promised to post here the part about GNU toolkit switches for diagnostic output:

--- clip clip ---

The GNU tools are very flexible, but it needs some tuning to the make files to get the desired results. Here are my switches for GCC plain C compilation:

CFLAGS = $(INCDIR) -g -Wall -Wstrict-prototypes CFLAGS += -Wa,-adhlms=$(@:.o=.lst) CFLAGS += -ffreestanding -fno-guess-branch-probability CFLAGS += -W -Wpointer-arith

The flags for GNU as via the gcc driver and pre-processor (.S files):

ASFLAGS = $(INCDIR) -g -Wa,-ahlms=$(@:.o=.lst)

The flags for linking via the gcc driver:

LDFLAGS = -g -nostartfiles -L.. -L../../lib -lm LDFLAGS += -Wl,-Map=$(TGT).map,--cref,-T,myscript.ld

Please note that there must be no whitespace inside the -Wa and -Wl arguments.

The flags create assembly listings into .lst files so you can see what the compiler did. The linker flags create a cross-reference into the map. I recall that you asked for it in the news. The library paths in the linker scripts have to be tailored to match your set-up.

--- clip clip ---

--
Tauno Voipio
tauno voipio (at) iki fi
 Click to see the full signature
Reply to
Tauno Voipio

And the -Wl,-Map= stuff was very useful. I don't need to address this at the moment, but should I care to do so, I have some good tools for shaking the box.

--
Tim Wescott
Wescott Design Services
 Click to see the full signature
Reply to
Tim Wescott

I mostly chose gnu tools because I'm a cheap bastard, but I also chose them to force me to learn more about them. Half of the tool chains out there seem to be based on gnu tools, and with the ARM seemingly taking over the world, and being such a good fit for the tool chain, I see a lot of value in becoming the go-to guy for that tool chain.

--
Tim Wescott
Wescott Design Services
 Click to see the full signature
Reply to
Tim Wescott
[...]

what is the "m" flag?

Do you also experience a clobbered listing when the C sources contain asm instructions?

-ffreestanding kills also builtins. Do you re-enable them?

Do you have any experience with -fno-guess-branch-probability ?

Oliver

--
Oliver Betz, Munich
despammed.com is broken, use Reply-To:
Reply to
Oliver Betz

I mostly choose gnu tools because I like them better, and I find they do a better job than many commercial tools. The price is of course nice - especially if I am doing stuff at home. Free tools means it is easy to install them on my work PC, and my home PC, and a laptop, etc., without ever having to think about whether it is cost-effective. It also means there is no administrative overhead involved in "purchasing" the tools - you don't need to fit them into budgets, arrange purchasing orders, etc.

I use a wide range of processors in my work - having gcc for almost all of them makes life easier for me. I can use the same editors, and the same procedures (and to a fair extent, the same makefile) for all the projects.

I have used (and still use) a fair number of commercial tools over the years. Details vary wildly, but gcc generates roughly as good code for most 32-bit targets (and it's not too bad even for the 8-bit avr). I also find some gcc enhancements and newer standard C features let me write clearer source code and smaller or faster target code. It's always annoying when I have to back to a commercial compiler that doesn't support things like intermixing variable declarations and executable code.

Reply to
David Brown

The "-W" flag is for older gcc versions - newer versions deprecate it replace it with "-Wextra". I can't remember off-hand which versions use which flag, but for new projects it's best to call it "-Wextra".

I like to add a number of other warning flags - depending on your style of coding, you may or may not find them useful.

There are some types of cast that are legal in C, but which I prefer to write explicitly if I need them. These warnings help spot mistakes:

-Wbad-function-cast -Wcast-qual -Wcast-align -Wpointer-arith

I am very strict about prototypes for functions - in my code, a function either has an "extern" declaration in the module's header file, or it is declared "static" in the module itself. These warnings enforce that policy :

-Wmissing-prototypes -Wmissing-declarations -Wredundant-decls

-Wnested-externs

I also like to be explicit about padding in structs: -Wpadded

Unreachable code is /mostly/ a sign of an error, but not always - sometimes I use "-Wunreachable-code". Older versions of gcc can give a lot of false positives here when inlining lets it remove extra code - newer versions give more accurate warnings.

"-Wc++-compat" is good if you are mixing C and C++ code.

Each new version of gcc gets more warning flags - it's worth having a little look in the manual for the version you are using.

Reply to
David Brown

It was a macro listing flag - seems to be dropped in current version of as.

Is this messed up:

/* Change processing priority, return old priority */

uint32_t change_pri(uint32_t newpri) { uint32_t oldpri;

__asm volatile ( "mrs %[r], basepri\n\t" /* get old priority */ "msr basepri, %[n]" /* update priority */

: [r] "=&r" (oldpri) : [n] "r" (newpri) );

return oldpri; }

----------------

427 .section .text.change_pri,"ax",%progbits 428 .align 1 429 .global change_pri 430 .thumb 431 .thumb_func 433 change_pri: 434 @ args = 0, pretend = 0, frame = 0 435 @ frame_needed = 0, uses_anonymous_args = 0 436 @ link register save eliminated. 437 @ 63 "kernel.c" 1 438 0000 EFF31183 mrs r3, basepri 439 0004 80F31188 msr basepri, r0 440 @ 0 "" 2 441 .thumb 442 0008 1846 mov r0, r3 443 000a 7047 bx lr

----------------

-Os uses thos built-ins that are sensible in embedded code.

It limits the eagerness of the optimizer to re-order branches in a non-obvious way, the effect is pretty small.

--

Tauno Voipio
Reply to
Tauno Voipio

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.