Author Topic: [gcc C] I have been blindly doing this but how does it work  (Read 1768 times)

0 Members and 1 Guest are viewing this topic.

Offline westfw

  • Super Contributor
  • ***
  • Posts: 2817
  • Country: us
Re: [gcc C] I have been blindly doing this but how does it work
« Reply #25 on: October 11, 2018, 10:24:15 am »
ARM missed the chance to add true single-bit variables as a extension to their compilers, in conjunction with the "bit-banding" hardware feature.
It could've worked really nicely - pointers to single bits would have worked fine, and everything.But it would have been non-standards-conforming, and seemed to mostly be aimed at IO ports that were better handled via other mechanisms, so they left it for users to do on their own, and so it mostly "didn't happen."  And now most new ARMs don't even implement bit-banding, so the the opportunity is gone...
 

Online langwadt

  • Super Contributor
  • ***
  • Posts: 1089
  • Country: dk
Re: [gcc C] I have been blindly doing this but how does it work
« Reply #26 on: October 11, 2018, 10:37:58 am »
ARM missed the chance to add true single-bit variables as a extension to their compilers, in conjunction with the "bit-banding" hardware feature.
It could've worked really nicely - pointers to single bits would have worked fine, and everything.But it would have been non-standards-conforming, and seemed to mostly be aimed at IO ports that were better handled via other mechanisms, so they left it for users to do on their own, and so it mostly "didn't happen."  And now most new ARMs don't even implement bit-banding, so the the opportunity is gone...

afaiu bit banding was dropped because it doesn't play well with data caches
 

Offline tombi

  • Regular Contributor
  • *
  • Posts: 152
  • Country: au
Re: [gcc C] I have been blindly doing this but how does it work
« Reply #27 on: October 11, 2018, 10:52:13 am »
Actually some compilers (like the Microsoft dinosaur I use for work) will give you a ‘performance warning’ if you assign an int to a bool. I think the compiler uses some tricks to make manipulating bool types faster.

If the compiler in question supports the bool type and C99, it's not completely a dinosaur. Microsoft's older compilers are known not to support C99 very well though, or only partially.

In your example, the warning makes perfect sense and I think you considered it backwards. As newbrain pointed out and I discussed afterwards (and showed an example), assigning an integer to a bool will do an implicit conversion and thus will "clip" the integer value to one if it's different from zero. This clipping operation costs more than a simple assignment. Thus the performance warning.

No! Very much not what I meant. I think that using bool adds readability and might even provide a speed-up as the compiler can make assumptions about bool types (i.e. they are effectively just a single bit).

I'd go further in fact and advocate explicit conversions so for example if you have an expression that is non-boolean, (like the result of a bit mask operation) then I think it is good to explicitly convert it to a boolean value by comparing it with zero. This means you are doing the conversion where you choose and you reduce the chance of weird operator precedence issues like it multiplying a number by a the result of a boolean expression.

Tom

 

Offline brucehoult

  • Frequent Contributor
  • **
  • Posts: 947
  • Country: nz
  • Currently at SiFive, previously Samsung R&D
Re: [gcc C] I have been blindly doing this but how does it work
« Reply #28 on: October 11, 2018, 02:46:08 pm »
#define BIT_GET(p,m) ((p) & (BIT(m)))
#define BIT(x) (0x01 << (x))

So I get the bit masking stuff but but how is some random byte that is the result is understood as a boolean state.

So 01010101 & 00000100 = 00000100

How does 00000100 equate the Boolean state of 1 ?

If you want to get a result of only 00000000 or 00000001 then use this instead:

Code: [Select]
#define BIT_GET(p,m) (((p)>>(m)) & 1)

That's a bit more work for the CPU as the shift is done at run time instead of compile time. If you've got a proper multi-bit shifter then it's likely to be only one extra instruction/cycle and so not to be worried about.
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 2817
  • Country: us
Re: [gcc C] I have been blindly doing this but how does it work
« Reply #29 on: October 11, 2018, 03:41:06 pm »
Quote
#define BIT_GET(p,m) (((p)>>(m)) & 1)
That's a bit more work for the CPU as the shift is done at run time instead of compile time.
If m is a constant, the compiler will optimize weirdly for you...
Code: [Select]
bool get_b6() {
    return BIT_GET(PORTB, 6);
   0:   85 b1           in      r24, 0x05   ;; read portb
   2:   86 fb           bst     r24, 6        ;; set T status bit from bit 6
   4:   88 27           eor     r24, r24    ;; zero return value
   6:   80 f9           bld     r24, 0        ;; load bit 0 (true or false) from T
   8:   08 95           ret
I get versions using a shift & and (b1), just an and (b0), and the carry bit (b7)


Quote
If you've got a proper multi-bit shifter then it's likely to be only one extra instruction/cycle and so not to be worried about.
Alas, no "proper" shifter on any of the AVR chips.  going to a variable bit number gets you a loop.  Computed with a 16bit int, too.  Sad.
Code: [Select]
bool get_bv(uint8_t b) {
    return BIT_GET(PORTB,b);
  2c:   25 b1           in      r18, 0x05       ; 5
  2e:   30 e0           ldi     r19, 0x00       ; 0
  30:   a9 01           movw    r20, r18
  32:   00 c0           rjmp    38
  34:   55 95           asr     r21
  36:   47 95           ror     r20
  38:   8a 95           dec     r24
  3a:   02 f4           brpl   34
  3c:   ca 01           movw    r24, r20
  3e:   81 70           andi    r24, 0x01       ; 1
  40:   08 95           ret

 

Offline tombi

  • Regular Contributor
  • *
  • Posts: 152
  • Country: au
Re: [gcc C] I have been blindly doing this but how does it work
« Reply #30 on: October 11, 2018, 04:01:43 pm »

Why not

Code: [Select]
#define BIT_GET(p,m) ( (p) & (BIT(m)) != 0 )
#define BIT(x) (0x01 << (x))
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 2817
  • Country: us
Re: [gcc C] I have been blindly doing this but how does it work
« Reply #31 on: October 11, 2018, 04:47:30 pm »
Needs another set of parens, after which it produces exactly the same code (for AVR) as the previous macro.
 

Offline tombi

  • Regular Contributor
  • *
  • Posts: 152
  • Country: au
Re: [gcc C] I have been blindly doing this but how does it work
« Reply #32 on: October 11, 2018, 04:55:22 pm »
Ok yes brackets...

Reasonably surprised that the generated code is the same as the previous definition -  #define BIT_GET(p,m) (((p)>>(m)) & 1).

The BIT() macro should evaluate to a compiler constant.

Then BIT_GET(p,m) assuming M is a constant evaluates to ((p & constant) != 0)

I only suggest adding the !=0 as then the result is a boolean and not a number.

So I assume it is used like

if ( BIT_GET(something,BIT(3))  )
{
...


 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 2817
  • Country: us
Re: [gcc C] I have been blindly doing this but how does it work
« Reply #33 on: October 11, 2018, 05:42:09 pm »
yep.  As you say.  The macros evaluate to the same code on AVR, CM0, and CM4. (all gcc, so that's not really surprising.)CM4 ends up using the "bit field extract" instruction, which is pretty nice.
here's code that should be pretty platform-independent:
Code: [Select]
#include <stdint.h>

volatile uint8_t PORTB;

#define BIT_GET(p,m) ( ((p) & (BIT(m))) != 0 )
#define BIT(x) (0x01 << (x))

#define BIT_GET1(p,m) ((((uint8_t)p)>>((uint8_t)m)) & (uint8_t)1)

bool get_b3() {
    return BIT_GET(PORTB, 3);
}
bool get1_b3() {
    return BIT_GET1(PORTB, 3);
}

bool get_bv(uint8_t b) {
    return BIT_GET(PORTB,b);
}
bool get1_bv(uint8_t b) {
    return BIT_GET1(PORTB,b);
}

int main() {}
extern "C" { void _exit(int c) {while(1);} }
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 363
Re: [gcc C] I have been blindly doing this but how does it work
« Reply #34 on: October 11, 2018, 08:32:51 pm »
Code: [Select]
#include <stdint.h>
#include <stdbool.h>
static inline bool bit_get(volatile uint8_t* p, uint8_t b){
    return *p & (1<<b) ? 1 : 0;
}
static inline void bit_set(volatile uint8_t* p, uint8_t b, bool tf){
    if(tf) *p |= (1<<b); else *p &= ~(1<<b);
}
void main()
{
    //just guessing that 0x24 will get me porta, 0x25 portb (IN/OUT instructions)
    //PORTA.3 = PORTB.6
    bit_set((volatile uint8_t*)0x24, 3, bit_get((volatile uint8_t*)0x25,6));

    for(;;);
}
/*
Disassembly of section .text:

00000000 <main>:
   0:   2e 9b           sbis    0x05, 6 ; 5
   2:   02 c0           rjmp    .+4             ; 0x8 <__zero_reg__+0x7>
   4:   23 9a           sbi     0x04, 3 ; 4
   6:   01 c0           rjmp    .+2             ; 0xa <__zero_reg__+0x9>
   8:   23 98           cbi     0x04, 3 ; 4
   a:   ff cf           rjmp    .-2             ; 0xa <__zero_reg__+0x9>
*/
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 2817
  • Country: us
Re: [gcc C] I have been blindly doing this but how does it work
« Reply #35 on: October 11, 2018, 08:50:45 pm »
Yeah; except your use of inlines, and compiler optimization, means that you never actually have a "bool."
I figure I'm a bit upset that I don't get:
Code: [Select]
   ldi r24, 1        ;; assume true   sbis PORTB,6  ;; test IO bit
    xor r24, r24   ;; we were wrong; set false.   ret
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 363
Re: [gcc C] I have been blindly doing this but how does it work
« Reply #36 on: October 11, 2018, 10:48:31 pm »
Quote
bool sw_is_on()
{
    return bit_get((volatile uint8_t*)0x25, 6);
}

/*
00000000 <sw_is_on>:
   0:   85 b1           in      r24, 0x05       ; 5
   2:   86 fb           bst     r24, 6
   4:   88 27           eor     r24, r24
   6:   80 f9           bld     r24, 0
   8:   08 95           ret
exact same result as the macro
 

Offline ahbushnell

  • Frequent Contributor
  • **
  • Posts: 379
  • Country: us
Re: [gcc C] I have been blindly doing this but how does it work
« Reply #37 on: October 14, 2018, 06:18:13 am »
#define BIT_GET(p,m) ((p) & (BIT(m)))
#define BIT(x) (0x01 << (x))

So I get the bit masking stuff but but how is some random byte that is the result is understood as a boolean state.

So 01010101 & 00000100 = 00000100

How does 00000100 equate the Boolean state of 1 ?
Its bit wise.  So each bit is operated on by its position.

Sent from my SM-G930V using Tapatalk

 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf