Arduino / compilers on uC

fc

01 movw r30,

fd

4f sbci

fc

01
55 1014: fd 4f

lpm r18, Z 1018: fc 01

0x4B ; 75 101c: fd 4f

lpm r30, Z 1020: ee

; 0x1026

breq .+78 ;

91 f0 breq .+36

a9 f0 breq .+42

01 f5 brne .+64
8f 7d andi r24,
0x1062
91 f0 breq .+36

a1 f0 breq .+40

b9 f4 brne .+46

03 c0 rjmp .+6
80 93 80 00 sts

8f 77 andi r24,
36 1064: 09 c0
03 c0 rjmp .+6
80 93 b0 00 sts

ee

0f add r30, r30

subi r30, 0x55 ;

a5

91 lpm

in r24, 0x3f ; 63

r30, X 108c: 61 11

0x1096

and r18, r30 1094: 01

st X, r18 109a: 8f

ret

I haven't read the whole thread, but clearly there are one or two elements missing from "get a trustworthy compiler and a trustworthy set of libraries, then trust the optimizer". (And it's not "trust the optimizer").

--
Tim Wescott 
Control systems, embedded software and circuit design 
I'm looking for work!  See my website if you're interested 
http://www.wescottdesign.com
Reply to
Tim Wescott
Loading thread data ...

I was mostly posting about the output quality of the tool chain, and only incidentally about the compiler bug (that seems to have captured all the attention).

I still maintain the compiler's wrong to only let me modify (at most) one bit of a boolean, but then later *itself* use all eight bits to decide if the variable is 'true' or 'false.'

I'll have more time to comment on this later--I've just powered up the instrument this is all in, a pulse gadget with four computer-set HV power supplies. It's like a mad chihuahua--vicious, but cute.

Cheers, James Arthur

Reply to
dagmargoodboat

And you were wrong on both accounts. You got inefficient code, and you had a bug in your code. That's fair enough - not many people write perfect code every time, and sometimes it is hard to figure out what went wrong. But you blamed the toolchain quality and compiler bugs - the toolchain (compiler, library, assembler, linker) is not at fault here.

First, your inefficient code for port output is due to the Arduino system's abstraction layers. That is part of the cost of using the flexible Arduino system. You can well say that the Arduino libraries are less efficient than they could be, or less well documented than they should be - I have no argument there. But you cannot compare assembly for setting a fixed output pin to code that uses run-time configurable choices of pins. It is an apples to grocery shop comparison. For the same task - turning on a fixed output pin - the compiler will generate the same optimal code as you would write by hand in assembly.

Secondly, your "compiler error" was your misunderstanding of how "bool" works in C++ (or _Bool in C), leading to a mistake in sanitizing data from an external source. Again, the Arduino system may have made it harder to figure out the problem, but the compiler was not at fault.

Development is hard - sometimes we hit problems that we do not understand. But the way forward is to ask "why is this happening?", not complain "/I/ can't be wrong - the tools are bad".

No, you are wrong here. Complain to the C standards committee that wrote C99, or the C++ designers that introduced "bool" (which C's _Bool copies). You actually would have to go back a bit and complain to Denis Ritchie, since the idea that the C compiler should believe what you tell it, based on the C language, is there from the start of the C story.

It's good that you have got it all sorted out in the end.

But if you don't learn from the mistakes here, you will make them again.

Reply to
David Brown

No it isn't. The example I gave was, that's true. But having looked at more of the generated code, it's not particularly efficient on this

8-bit machine.

Assume 'b' contains 0xff. What should the following code segment print?

bool b; word w;

w = (word) b; w &= 1;

println(w);

Cheers, James Arthur

Reply to
dagmargoodboat

There are certainly some areas where the code generated by avr-gcc is not as good as it could be. Part of that is limitations of the AVR architecture (inefficient pointers, no SP+index addressing), part of it is limitations of the gcc architecture (such as register allocation being done in "int" sizes, leading to sub-optimal register usage for

8-bit data), and part of it is due to there only being a few people working on the avr port of gcc, and they simply don't have the capacity to tune the compiler as well as they would like. It is also the case that code generation can be quite sensitive to a number of compiler options - perhaps the Arduino software hides these.

Anyway, all I can say for sure here is that the inefficient code sample you gave is primarily due to Arduino's abstraction layers, and that when I change a port output in my own AVR C code, the generated assembly is optimal.

I can't assume "b" contains 0xff, because at this point there is no "b".

If this is within a function, then "b" is not initialised - I expect to see a warning indicating this on the "w = (word) b;" line. (If the Arduino software does not make it extremely simple to enable -Wall warnings, then it is a poor system indeed.)

I expect "w &= 1;" to do nothing, assuming optimisation is enabled.

The call to "println(w)" will therefore write whatever happened to be in the registers at the time. Of course, the compiler is also free to omit the call entirely - the program has undefined behaviour, and the compiler can do anything it wants.

What you do not understand here is that there is /no/ way to make "b" contain 0xff in a legal C program. It can't happen. You have to cheat

- and if you cheat, you cannot expect the compiler to see that. It is /your/ responsibility to ensure that a variable is properly initialised before you attempt to read from it. This can be done by normal C initialisation or assignment. If you use pointer casts, memcpy, inline assembly, unions, and other methods to set the variable such a way that you have put an invalid value in the variable, then your program is broken - /you/ broke it by allowing undefined behaviour. You cannot expect the compiler to figure out how /you/ broke the rules behind its back, and then to take that into account.

Reply to
David Brown

Rust seems to be the popular "modern compiled" language-du-jour among the code guys I know who stay on the ball with these things...;-)

Reply to
bitrex

On 15/02/17 16:02, David Brown wrote:

Just for fun, I've downloaded the latest Atmel packaging of avr-gcc (version 3.5.4, with avr-gcc version 4.9.2). I tested this source, compiled with:

avr-gcc -c b.c -Wall -Wextra -Os -Wa,-ahlsd=b.lst

Look at the generated object code. No matter how much you add "volatile", or try to muck about with forcing an invalid value into "b", the "w &= 1;" is omitted as redundant. The compiler generates a warning message for test1:

b.c: In function 'test1': b.c:13:4: warning: 'b' is used uninitialized in this function [-Wuninitialized] w = (word) b;

The listing file below shows the generated assembly. It is entirely correct, with the only inefficiency being the loading of r24:r25 with 0 in test1 (the compiler could have skipped that).

// b.c #include #include

typedef uint16_t word;

extern void println(word w);

void test1(void) { bool b; word w; w = (word) b; w &= 1; println(w); }

void test2(void) { extern bool bx; word w; w = (word) bx; w &= 1; println(w); }

void test3(void) { extern volatile bool bv; word w; w = (word) bv; w &= 1; println(w); }

void test4(void) { extern volatile bool bv; word w; *(volatile uint8_t*) &bv = 2; w = (word) bv; w &= 1; println(w); }

void test5(void) { volatile union { bool b; uint8_t u; } bu; word w; bu.u = 2; w = (word) bu.b; w &= 1; println(w); }

GAS LISTING /tmp/ccVWdIy0.s page 1

1 .file "b.c" 2 __SP_H__ = 0x3e 3 __SP_L__ = 0x3d 4 __SREG__ = 0x3f 5 __tmp_reg__ = 0 6 __zero_reg__ = 1 7 .text 8 .global test1 10 test1: 11 /* prologue: function */ 12 /* frame size = 0 */ 13 /* stack size = 0 */ 14 .L__stack_usage = 0 15 0000 80E0 ldi r24,0 16 0002 90E0 ldi r25,0 17 0004 00C0 rjmp println 19 .global test2 21 test2: 22 /* prologue: function */ 23 /* frame size = 0 */ 24 /* stack size = 0 */ 25 .L__stack_usage = 0 26 0006 8091 0000 lds r24,bx 27 000a 90E0 ldi r25,0 28 000c 00C0 rjmp println 30 .global test3 32 test3: 33 /* prologue: function */ 34 /* frame size = 0 */ 35 /* stack size = 0 */ 36 .L__stack_usage = 0 37 000e 8091 0000 lds r24,bv 38 0012 90E0 ldi r25,0 39 0014 00C0 rjmp println 41 .global test4 43 test4: 44 /* prologue: function */ 45 /* frame size = 0 */ 46 /* stack size = 0 */ 47 .L__stack_usage = 0 48 0016 82E0 ldi r24,lo8(2) 49 0018 8093 0000 sts bv,r24 50 001c 8091 0000 lds r24,bv 51 0020 90E0 ldi r25,0 52 0022 00C0 rjmp println 54 .global test5 56 test5: 57 0024 CF93 push r28 58 0026 DF93 push r29 59 0028 1F92 push __zero_reg__ 60 002a CDB7 in r28,__SP_L__ 61 002c DEB7 in r29,__SP_H__ 62 /* prologue: function */ 63 /* frame size = 1 */ 64 /* stack size = 3 */ 65 .L__stack_usage = 3 66 002e 82E0 ldi r24,lo8(2) GAS LISTING /tmp/ccVWdIy0.s page 2

67 0030 8983 std Y+1,r24 68 0032 8981 ldd r24,Y+1 69 0034 90E0 ldi r25,0 70 0036 00D0 rcall println 71 /* epilogue start */ 72 0038 0F90 pop __tmp_reg__ 73 003a DF91 pop r29 74 003c CF91 pop r28 75 003e 0895 ret 77 .ident "GCC: (AVR_8_bit_GNU_Toolchain_3.5.4_1709)

4.9.2" GAS LISTING /tmp/ccVWdIy0.s page 3

DEFINED SYMBOLS *ABS*:0000000000000000 b.c /tmp/ccVWdIy0.s:2 *ABS*:000000000000003e __SP_H__ /tmp/ccVWdIy0.s:3 *ABS*:000000000000003d __SP_L__ /tmp/ccVWdIy0.s:4 *ABS*:000000000000003f __SREG__ /tmp/ccVWdIy0.s:5 *ABS*:0000000000000000 __tmp_reg__ /tmp/ccVWdIy0.s:6 *ABS*:0000000000000001 __zero_reg__ /tmp/ccVWdIy0.s:10 .text:0000000000000000 test1 /tmp/ccVWdIy0.s:21 .text:0000000000000006 test2 /tmp/ccVWdIy0.s:32 .text:000000000000000e test3 /tmp/ccVWdIy0.s:43 .text:0000000000000016 test4 /tmp/ccVWdIy0.s:56 .text:0000000000000024 test5

UNDEFINED SYMBOLS println bx bv

Reply to
David Brown

David, you are an expert.

We've had enough of experts....

--

John Devereux
Reply to
John Devereux

There are two possible translations of that

1) we don't like people that use facts to point out where our gut feelings are wrong 2) we shouldn't need experts (of this kind) to implement systems.

The former is disreputable and the realm of snake-oil salesman.

The latter is either wishful thinking, or a statement that this toolset and associated /necessary/ "language lawyer" mentality is unacceptable. I tend to the latter.

It will be interesting to see if C/C++ survives[1] any move towards legal regulation of software (cf civil engineering). One perspective:

formatting link
it will be interesting to see if it gains traction.

[1] short term of course it will survive. But long term is less clear, particularly as better tool arrive.
Reply to
Tom Gardner

That is the one I was going for. Along the lines of, we have our own alternative facts.

It was of course an excellent post, informative as always from David!

I have been programming in C for a long time in embedded systems, but it is only a small part of my job. I don't consider myself an "expert" and certainly not a language lawyer.

I find that sticking to a few conventions and idioms is enough to avoid issues like the OP had with bool and so forth. In this case, as David says, don't lie to the compiler. Be hyper-vigilent when writing a cast, it is very likely the wrong way to do things and usually dangerous and not necessary.

I find C to be fairly pleasant and surprise-free in use.

--

John Devereux
Reply to
John Devereux

That is a strong tradition in s.e.d. :-)

Thank you - it was intended to be helpful.

Of course, "expert" is a relative term. I know more of the details of C standards, compilers, and "legalese" than most C programmers, and most people in this group - but over in comp.lang.c I am much lower in the rankings.

(And my "expertise" is by no means infallible - the C11 draft standard N1570 is freely available online.)

I do the same - I just like to know why the idioms work.

Yes - it's a good rule.

True, in general.

However, the cast was not the problem here - it was not even necessary. There is no difference between "word w; w = (word) b;" and "word w; w = b;" or simply "word w = b;". There are only two reasons for using a cast here - either the programmer doesn't really understand casts and conversions in C, or the programmer wants to make the conversion abundantly clear to the reader by adding it explicitly as a sort of documentation.

It usually is, unless you try to push it and break the rules, or unless you think the compiler is supposed to translate your source line-by-line into assembly.

Reply to
David Brown

My career trajectory meant I was a heavy user of C in the early-late 80s, but in the 90s I moved onto software for which C was a less good fit.

In the noughties I saw some truly horrific commercial code in C/C++, much of it written by youngsters that hadn't grown up with it - and who had little conception of what was happening "under the hood". Even FSMs and caches were things they were only vaguely aware of :(

In such cases, a more modern higher level language tended to enable systems with fewer /undiagnosed/ surprises lurking in the undergrowth. Such surprises, if not dealt with by they programmer, were at least visible in the source code.

Agreed; that's my experience when writing small applications in C. But when I can't write all the code myself I have to rely on the competence of unknown other programmers. And that's a real problem given my observations above!

I don't have experience of MISRA-based systems, and I don't know how many of the fun and games outlined in the FQA are trapped out.

It gets /much/ nastier on machines with multiple levels of memory hierarchy, including caches, local memory and remote memory - and, of course, multiprocessors.

When considering those factors, the compiler writers and compiler users have difficulty agreeing what is "meant" by the source code. Doubly so when different (generations of) compiler are used.

Reply to
Tom Gardner

That is when I learned all the bad habits I later had to unlearn - using C on crappy 8 bit processors with only a few registers and not much help with optimisation from the inefficient buggy compilers.

So the shortage of RAM and index registers meant my code had lots of global 8bit variables for "efficiency", since the CPUs could not do indexed or stack based addressing at all well. So code was faster and shorter with variables in fixed absolute addresses. Stack was limited so one tended towards long functions instead of breaking things up into shorter ones. Etc.

The I used the AVR of the thread. This was slightly more modern but still not perfectly suited for C. For example its 8-bit registers are a poor match for C's general arithmetic types and the different types of memory area require some nasty kludges IIRC.

I think the current day cortex M CPUs are probably more like the minicomputers C was designed for (but much faster). C and c++ are a perfect match here, with gcc an excellent and free compiler.

I tend to use C on devices using up to ~256k code, so far.

--

John Devereux
Reply to
John Devereux

I don't have many quibbles with that, but problems begin to creep in when A9 (etc) come into the equation. Even mid-range FPGAs have dual-core A class processor in them nowadays.

However, I've just deleted an email titled "Preconfigured Cloud Solutions Get Your IoT Project to Market Easier"

That is an indication that the imperative is towards use-and-forget-without-understanding where subtle bugs will lie for youngsters without the time/inclination to learn when PHBs are breathing down their neck.

No, I don't like that either.

C/C++ is touted as being suitable for everything. Those that have had to debug foul problems in other people's code disagree.

Reply to
Tom Gardner

Overheard today: "The 11 in C++11 refers to the number of legs that have now been nailed onto the dog whilst attempting to build a better octopus."

:)

Reply to
Clifford Heath

There's two issues here that I know of. The first is that the optimizer is constrained by the difference between the target machine and how closely it resembles a C ideal machine. The C ideal machine has at least

16 bit integers, 8-bit 'puters don't. The second is that if you're going to write code that's fast on odd word size machines (and 8 bits _is_ odd word size for C), then you need to tell the compiler your intentions.

Heavy use of int_fast8_t is indicated, particularly if you want the code to be efficient on both an 8-bit machine and wider word-width machines.

If the processor you're using is _truly_ not a good fit for the C ideal machine (i.e., if it's a PIC or something), then any fully-compliant C is going to be less efficient than assembly. I'm not user that the AVR really fits that category.

--
Tim Wescott 
Control systems, embedded software and circuit design 
I'm looking for work!  See my website if you're interested 
http://www.wescottdesign.com
Reply to
Tim Wescott

As the man said (paraphrasing Shakespeare), "First let's kill all the language lawyers." ;)

Cheers

Phil Hobbs

Reply to
pcdhobbs

I'd prefer to "dispose of" the reason they are necessary :(

Reply to
Tom Gardner

I have worked with low-level programming on dual-core processors with caches. There are things that you need to take into account, but it is perfectly possible to write C (or C++) on them. The key point is to realise that "volatile" does /not/ mean "make the chip do what I want".

Then you need to learn how caches and interaction between the cores and the hardware work, and what magic incantations are needed to get the communication in the right order. If you are lucky, you have an RTOS and you use the functions from that. Less lucky, and you use library functions or compiler built-in functions. Unlucky, and you write your own inline assembly functions and use them.

Reply to
David Brown

No surprises there! But "perfectly possible" doesn't change any of the points I've been making. In practice I would want to examine the compiler's output and compare it with (my understanding of) the hardware. Rinse and repeat for different architectures and compilers.

My preference has been to use RTOSs, but even there there can be problems. I once had to diagnose a problem (probably unrelated to this discussion, but I'll never know) by disassembling what I could see happening on the bus. One value "entered" the RTOS, and later on another "emerged". Fun fun fun.

Reply to
Tom Gardner

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.