Assembler vs C language a bit of advise....

Go To Last Post
82 posts / 0 new

Pages

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have seen a lot of C code (especially around ISR's), where people ask why doesn't this work​.....

I have known people that think they can show how clever they are if they can produce C 'write only' code. 

 

David

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:
C is very good at high level programming, where the structure is like a human think.

ASM is very good at HW close things, where the best structure follow the hardware.

And always write a good documentation for both, C and assembler. This should help also for "easy" parts of code. If you have a look at the code some time later you may not remember why one part was coded like it is... but without checking the complete code it might a bad idea to optimize this part, i.e. if you use a varaible or a register 100+ lines later and expect a special value...

 

But I know from my own that documentation is a very hard thing, sometimes even more then planning and figuring out the math algorythms. ;)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The problem is that if you forget things like volatile etc. the code will work without optimizing, but NOT when optimized, and the error is your not the compiler.

 

The newer GCC compilers have a -Og that you should use for debuging 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:
My point is, it's not difficult to beat the compiler, but usually it's not worth the effort.

Now, hold on a bit.  Did the compiler carry out what you told it to do in an efficient manner?  I'd say pretty efficient.

 

Because you told it to do things in one way isn't the compiler's fault.  If I really wanted to byte-swap as an important part of the application (ModbusRTU comes to mind -- big-endian data and little-endian processor) then I'd use a union.  Or several other approaches including XOR-swap.

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Thu. Jul 28, 2016 - 01:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:
Even so, I could easily do it better in assembly.

<main>:
	ldi     r30, 0x00       
	ldi     r31, 0x01       
	ld      r25, Z
	ldd     r24, Z+1        
	st      Z, r24
	std     Z+1, r25        
	ret

My point is, it's not difficult to beat the compiler, but usually it's not worth the effort.

-fsplit-wide-types might improve avr-gcc's code.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

El Tangas wrote:
My point is, it's not difficult to beat the compiler, but usually it's not worth the effort.

Now, hold on a bit.  Did the compiler carry out what you told it to do in an efficient manner?  I'd say pretty efficient.

 

Because you told it to do things in one way isn't the compiler's fault.  If I really wanted to byte-swap as an important part of the application (ModbusRTU comes to mind -- big-endian data and little-endian processor) then I'd use a union.  Or several other approaches including XOR-swap.

 

 

So the compiler generates a code sequence that is 20 bytes long a takes 14 cycles. I look at that code, see that it could be improved by removing a couple of instructions and rearranging registers, and it now takes 10 cycles and is 12 bytes long. I saw an optimization the compiler missed, simple. Am I supposed to be impressed by the compiler for it's reasonable code? Well, maybe, I wouldn't be able to write a C compiler for sure cheeky

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't think this thread is about "writing a better C compiler". ;)

 

For sure, a lot of compiled C code might be improved. But building a perfect compiler and optimizer seems to be impossible.

 

What does a compiler do? It has to analyze the C code and replace the code with assembler instructions. Optimizing is an other step, usually it will check the code before converting it and also it has to check the compiled part. I think this will work for a lot of code pretty well, but I think also this is not valid for every code. ;)

 

Buit this is not the point. C code is a good language for writing fast, efficient and short code and as we saw on the "simple" 64x64 multiplication example the assembler code has a much longer source. But it might be improved.

 

It still depends on the goal which we have in mind. Do I need code which is simple to read and to understand or do I need code which is very predictive so I can calculate the exectuion time exactly?

 

In my case I like to use an ATTiny85 (TSSOP) because of the space and the amount of pins (I just need five I/O's, ok six would also be good). And I will also use a small battery (LiPo with something about 50mAh - 100mAh). So the more efficient the code is the better is it for the r/c modell.

 

But first of all, before optimizing, I have to get everything together and write the complete code. The first version might the C code since it seems to be easy and the uC is fast enough, later maybe I can reduce the clock speed, but than it might be difficult to get (more or less) exact time meassurement.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

El Tangas wrote:
Am I supposed to be impressed by the compiler for it's reasonable code?

No the compiler generated what you wrote. If you wrote the "right thing" it would likely have generated the "right thing". That was Lee's point. Don't assume that simply the first thing you put down on paper or tap into your C editor is going to be the perfect solution. You would need to understand the code generation model of the compiler very deeply to be able to write C immediately that you knew could not be improved. But 99.9% of the time what the compiler generates is quite satisfactory and it saves you adding tons of obfuscation to the source just to get 2 cycles or 4 bytes saved or whatever. C is about writing code that it's easy for others to read and maintain.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

gcc will optimize code better than the average asm programmer, they say...

 

void foo(uint8_t *buffer, uint8_t cnt)
{
	while(--cnt)
	{
		*buffer++ = uart0_getc();
	}
}

 

000000a4 <foo>:
  a4:	0f 93       	push	r16
  a6:	1f 93       	push	r17
  a8:	cf 93       	push	r28
  aa:	df 93       	push	r29
  ac:	1f 92       	push	r1
  ae:	cd b7       	in	r28, 0x3d	; 61
  b0:	de b7       	in	r29, 0x3e	; 62
  b2:	8c 01       	movw	r16, r24
  b4:	61 50       	subi	r22, 0x01	; 1
  b6:	39 f0       	breq	.+14     	; 0xc6 <foo+0x22>
  b8:	69 83       	std	Y+1, r22	; 0x01
  ba:	4e d0       	rcall	.+156    	; 0x158 <uart0_getc>
  bc:	f8 01       	movw	r30, r16
  be:	81 93       	st	Z+, r24
  c0:	8f 01       	movw	r16, r30
  c2:	69 81       	ldd	r22, Y+1	; 0x01
  c4:	f7 cf       	rjmp	.-18     	; 0xb4 <foo+0x10>
  c6:	0f 90       	pop	r0
  c8:	df 91       	pop	r29
  ca:	cf 91       	pop	r28
  cc:	1f 91       	pop	r17
  ce:	0f 91       	pop	r16
  d0:	08 95       	ret

(4.9.2 with -Os flag)

Last Edited: Thu. Jul 28, 2016 - 06:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jnk0le wrote:
gcc will optimize code better than the average asm programmer, they say...

I cannot tell what point you are trying to make with that snippet.  Impressed?  Disgusted?  Meh?

 

It looks at least "good" to me, in taking what you told the compiler to do and producing good code according to the rules of C and its underlying model of code generation.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 You would need to understand the code generation model of the compiler very deeply to be able to write C immediately that you knew could not be improved.

 

That is exactly the problem and strength with C, it has a good robust way to structure code, but it's very rare it's optimal.

 

In ASM you can structure the program after the problem you want to solve.

 

If you just take the C compiler's output and optimize you don't gain much, but often an other structure will solve the problem in a better way (size speed or both). 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That sounds ok.

But writing a complete word processing program (like an office suite) in assembler on a Windows / Unix computer sounds impossible...

 

For small micro controllers it might be ok, depending on the problem - and developer. :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

derniwi wrote:
But writing a complete word processing program (like an office suite) in assembler

Been there, done that, worn the T-shirt...

 

http://toastytech.com/guis/pcw.html

 

That is all written in Z80 assembler. Trying to get the WYSIWYG word-processor right was a "challenge"!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Two questions to ponder:  ​​

 

  1. If Assembler is the ideal environment for developing software why are there high level languages? 
  2. If a high level language is the ideal environment for developing software why is there in line assembler?  

 

David 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I agree with argument 1. If Assembler were the choice none of us would ever have seen the need to learn C. But 2 is surely just a historical thing dating from the time when C implementations might not be "complete" and you might want to revert to Asm for things it was not initially that easy to achieve in C. It also stems from the way C compilers work - they convert C to Asm then assemble it. So it's trivial to offer a function to just say "insert this raw Asm sequence into the intermediate Asm file you are about to assemble".

 

In a professional project I work on there are about 50,000 source files and perhaps 10,000,000 NCLOC generating perhaps 10MB of code. In all of that (admittedly C++ not C) I don't think we actually use inline Asm anywhere. So just because a facility exists does not necessarily mean it's being used.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is also a goto stament in C, but I don't have used it (ok I'm not a professional programmer).

:)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think you are answering the questions there. 

 

The project you describe doesn't need to use inline Assembler so it doesn't use it however if you need to talk to hardware at a low level and need maximum speed for the routine you use inline assembler.  Horses for courses. 

 

Many years ago on a project written in Coral and destined for Software Validation (so no interrupts) the structured nature took longer to get to the hardware timers to set a time than the time being set; consequently the timers were set using inline Assembler. 

 

David

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jnk0le wrote:
gcc will optimize code better than the average asm programmer, they say...

You can -fno-caller-saves to improve the generated code (still not optimal).  I never saw -fcaller-saves (default) improving the code.

 

When I have some days left over I will propose to switch off -fcaller-saves in avr-gcc, it's just a 1-liner. If all goes well it will be part of avr-gcc v7.

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

With this flag the code looks a little bit better, but Y pointer is stil wasted for a single 8 bit counter.

000000a0 <foo>:
  a0:	0f 93       	push	r16
  a2:	1f 93       	push	r17
  a4:	cf 93       	push	r28
  a6:	c6 2f       	mov	r28, r22
  a8:	8c 01       	movw	r16, r24
  aa:	c1 50       	subi	r28, 0x01	; 1
  ac:	29 f0       	breq	.+10     	; 0xb8 <foo+0x18>
  ae:	76 d0       	rcall	.+236    	; 0x19c <uart0_getc>
  b0:	f8 01       	movw	r30, r16
  b2:	81 93       	st	Z+, r24
  b4:	8f 01       	movw	r16, r30
  b6:	f9 cf       	rjmp	.-14     	; 0xaa <foo+0xa>
  b8:	cf 91       	pop	r28
  ba:	1f 91       	pop	r17
  bc:	0f 91       	pop	r16
  be:	08 95       	ret

 

Is it a "feature" responsible of

gcc 4.x.x generates 30% bigger code than 3.x.x

or something else makes the regression?

Last Edited: Mon. Aug 1, 2016 - 03:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jnk0le wrote:
With this option the code looks a little bit better, but Y pointer is stil wasted for a single 8 bit counter.

???  >>You<< created the code fragment/function posted above in #60.  >>You<< told the compiler to update the "buffer" parameter.  >>You<< called a function in the middle, requiring more save/restore.

 

Please, if you are going to shoot out of context, post the ideal ASM sequence.  And ensure that it fulfills all the requirements that >>you<< gave to the C compiler.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
    push	r16
    push	r28
    push	r29

    mov 	r16, r22
    movw	r28, r24
L_A:
    subi	r16, 0x01 // r16 is call saved
    breq	L_B
    rcall	uart0_getc
    st  	Y+, r24 // Y pointer is call saved
    rjmp	L_A
L_B:
    pop	r29
    pop	r28
    pop	r16
    ret
Last Edited: Mon. Aug 1, 2016 - 11:14 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As said in #62, the model used by the compiler work.

 

But it has rules how to use Y and Z which often can be done better in ASM. (I differently use other rules for lifetime of those pointers ;)  )

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jnk0le wrote:
With this option the code looks a little bit better, but Y pointer is stil wasted for a single 8 bit counter.
theusch wrote:
>>You<< created the code fragment/function posted above in #60.

And you chose in your C code to reference and modify the "cnt" parameter.  In your ideal snippet, you chose to copy it to a temporary (R16).  Equivalent to copying the parameter value to a local in C.

 

[I couldn't tell you the rules of C on this one (whether the parameter "cnt" needs to be updated).  For "buffer" -- yes, it needs to update the value.  Whether it does it once at the end of the loop or each time -- I suppose the compiler could be smart enough to know that the loop will end at some point.]

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One of the advantages of assembly language over C is that the object code remains constant for given source code.  You don't run out of space in your bootloader suddenly because you tried to use a new compiler version, because the compiler decided to save registers that didn't really need to be saved, or count the bits in a loop that's supposed to be dependent on shift results, or whatever...   avr-gcc is especially troublesome, because the front-end code seems to keep being "improved" in ways that are difficult for the back-end to deal with efficiently.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The learning curve for writing assembler is much bigger than C.

But when you've gone up that learning curve, it gets easier and easier as you build a bunch of utility routines that you can call do do hard things.

Using MACROS is really nice, as it almost allows you do define your own high-level language with real simple syntax.

The benefit is you know exactly what the processor is doing blow by blow.

What C or C++ is having the processor do is somewhat obscured, and that sometimes leads to head scratching.

 

Also, C allows you to have only one Main program, but you can have as many as you like in assembler.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've written assembler and C for AVR. Assembler may be OK for very small programs, and for learning the instruction set, but the C compiler looks after the register usage, optimises the code well, and you can write at least ten times faster. The program from C is hardly bigger than assembler, and rather more readable

If you're a masochist write assembler

Jerry

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PaulWDent wrote:
What C or C++ is having the processor do is somewhat obscured, and that sometimes leads to head scratching.
Then you are doing it wrong. Compilers have very few quiet bugs.

Their semantics are defined by their languages and their implementation-defined features.

If that is not good enough, their assembly outputs are available.

Quote:
Also, C allows you to have only one Main program, but you can have as many as you like in assembler.
An AVR allows up to two Main programs: the one that starts at flash address zero and the one that starts at the beginning of bootloader space.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Michael

 

Be warned that Mr Dent appears to be something of a troll. He only visited the site for 1 day. Plastered about 25 posts across various threads (some of it utter nonsense) and then just as quickly disappeared again (maybe after some of my moderator coleagues  warned him!). So I'm guessing you aren't going to be seeing much in the way of replies.

 

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PaulWDent wrote:

Also, C allows you to have only one Main program, but you can have as many as you like in assembler.

 

How does that work?

 

Surely your assembler program will run from the reset vector and then make a choice of where to continue. Which is exactly what you can do in C.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As I say he appears to have been something of a troll. The thread that now seems inevitable is presumably exactly what such a post was trying to trigger in the first place. So he won! angry

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd start wih C. Especially when writing "advice".wink

Four legs good, two legs bad, three legs stable.

Pages