Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why a*b/c instead of a*(b/c) gives 3x bigger program size on AVR?

Recently I tried to pack my code into small ATTiny13 with 1kB of flash. In optimalisation process I discovered something weird for me. Let's take the example code:

#include <avr/interrupt.h>

int main() {
    TCNT0 = TCNT0 * F_CPU / 58000;
}

It has no sense of course, but interesting thing is output size - it produces 248 bytes.

Quick explaination of code: F_CPU is constant defined by -DF_CPU=... switch for avr-gcc, TCNT0 is 8-bit register (on ATTiny13). In real program I assign equation result to uint16_t, but still same behaviour was observed.

If part of expression were wrapped in brackets:

TCNT0 = TCNT0 * (F_CPU / 58000);

Output file size is 70 bytes. Huge difference, but results of these operations are same (right?).

I looked into generated assembly code and, despite fact that I don't understand ASM very well, I see that no-brackets version adds some labels like:

00000078 <__divmodsi4>:
  78:   05 2e           mov r0, r21
  7a:   97 fb           bst r25, 7
  7c:   16 f4           brtc    .+4         ; 0x82 <__divmodsi4+0xa>
  7e:   00 94           com r0
  80:   0f d0           rcall   .+30        ; 0xa0 <__negsi2>
  82:   57 fd           sbrc    r21, 7
  84:   05 d0           rcall   .+10        ; 0x90 <__divmodsi4_neg2>
  86:   14 d0           rcall   .+40        ; 0xb0 <__udivmodsi4>
  88:   07 fc           sbrc    r0, 7
  8a:   02 d0           rcall   .+4         ; 0x90 <__divmodsi4_neg2>
  8c:   46 f4           brtc    .+16        ; 0x9e <__divmodsi4_exit>
  8e:   08 c0           rjmp    .+16        ; 0xa0 <__negsi2>

And much more. I learned only x86 assembler awhile, but as far as I remember, for division there was simple mnemonic. Why avr-gcc adds so much code in first example?

Another question is why compiler does not inline right part of equation if both numbers are known in compile time.

like image 743
aso Avatar asked Jan 24 '23 23:01

aso


1 Answers

We have this:

x = x * 1200000 / 58000

Note that 1200000/58000 = 20.69... is not an integer, so this must be computed as first multiplying and then flooring dividing. Your architecture does not have native integer division for this data type, so it has to emulate it, resulting in a lot of code.

However this:

x = x * (1200000 / 58000)

we find that 1200000 / 58000 = 20, since C uses flooring division, so this code is simplified to just:

x = x * 20
like image 76
orlp Avatar answered Jan 29 '23 12:01

orlp