Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #161725 > unrolled thread

naked switches

Started byRobert Finch <robfi680@gmail.com>
First post2021-07-07 16:39 -0700
Last post2021-09-30 18:04 +0200
Articles 20 on this page of 59 — 11 participants

Back to article view | Back to comp.lang.c


Contents

  naked switches Robert Finch <robfi680@gmail.com> - 2021-07-07 16:39 -0700
    Re: naked switches Bart <bc@freeuk.com> - 2021-07-08 01:26 +0100
    Re: naked switches Kaz Kylheku <563-365-8930@kylheku.com> - 2021-07-08 01:57 +0000
      Re: naked switches David Brown <david.brown@hesbynett.no> - 2021-07-08 09:16 +0200
    Re: naked switches Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2021-07-08 01:00 -0700
      Re: naked switches Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2021-07-08 03:25 -0700
        Re: naked switches Robert Finch <robfi680@gmail.com> - 2021-07-08 06:58 -0700
          Re: naked switches Bart <bc@freeuk.com> - 2021-07-08 15:52 +0100
          Re: naked switches David Brown <david.brown@hesbynett.no> - 2021-07-08 17:52 +0200
            Re: naked switches Bart <bc@freeuk.com> - 2021-07-08 17:51 +0100
              Re: naked switches David Brown <david.brown@hesbynett.no> - 2021-07-09 10:01 +0200
                Re: naked switches Bart <bc@freeuk.com> - 2021-07-09 11:09 +0100
                  Re: naked switches David Brown <david.brown@hesbynett.no> - 2021-07-09 13:14 +0200
                  Re: naked switches Manfred <noname@add.invalid> - 2021-07-09 15:35 +0200
                    Re: naked switches David Brown <david.brown@hesbynett.no> - 2021-07-09 16:51 +0200
                      Re: naked switches Manfred <noname@add.invalid> - 2021-07-09 18:23 +0200
                    Re: naked switches Bart <bc@freeuk.com> - 2021-07-09 16:07 +0100
                      Re: naked switches Manfred <noname@add.invalid> - 2021-07-09 18:41 +0200
                        Re: naked switches David Brown <david.brown@hesbynett.no> - 2021-07-10 11:02 +0200
                      Re: naked switches David Brown <david.brown@hesbynett.no> - 2021-07-09 19:20 +0200
                        Re: naked switches Robert Finch <robfi680@gmail.com> - 2021-07-09 10:46 -0700
                          Re: naked switches Kaz Kylheku <563-365-8930@kylheku.com> - 2021-07-09 19:56 +0000
                          Re: naked switches Bart <bc@freeuk.com> - 2021-07-09 22:01 +0100
                            Re: naked switches Robert Finch <robfi680@gmail.com> - 2021-07-09 15:07 -0700
                              Re: naked switches Bart <bc@freeuk.com> - 2021-07-09 23:30 +0100
                          Re: naked switches David Brown <david.brown@hesbynett.no> - 2021-07-10 12:17 +0200
            Re: naked switches Robert Finch <robfi680@gmail.com> - 2021-07-08 10:18 -0700
              Re: naked switches Bart <bc@freeuk.com> - 2021-07-08 18:43 +0100
                Re: naked switches Robert Finch <robfi680@gmail.com> - 2021-07-08 15:32 -0700
              Re: naked switches Kaz Kylheku <563-365-8930@kylheku.com> - 2021-07-08 17:47 +0000
                Re: naked switches Robert Finch <robfi680@gmail.com> - 2021-07-08 15:45 -0700
                  Re: naked switches David Brown <david.brown@hesbynett.no> - 2021-07-09 10:27 +0200
                    Re: naked switches Ben Bacarisse <ben.usenet@bsb.me.uk> - 2021-07-09 11:13 +0100
                      Re: naked switches David Brown <david.brown@hesbynett.no> - 2021-07-09 13:30 +0200
                      Re: naked switches Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-07-11 00:39 -0700
                        Re: naked switches scott@slp53.sl.home (Scott Lurndal) - 2021-07-11 14:06 +0000
                          Re: naked switches Robert Finch <robfi680@gmail.com> - 2021-07-11 08:01 -0700
                          Re: naked switches Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-07-16 03:53 -0700
                            Re: naked switches scott@slp53.sl.home (Scott Lurndal) - 2021-07-16 14:47 +0000
                              Re: naked switches Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-09-30 06:09 -0700
                                Re: naked switches Robert Finch <robfi680@gmail.com> - 2021-09-30 07:03 -0700
                    Re: naked switches Bart <bc@freeuk.com> - 2021-07-09 13:44 +0100
                      Re: naked switches David Brown <david.brown@hesbynett.no> - 2021-07-10 13:28 +0200
                        Re: naked switches Bart <bc@freeuk.com> - 2021-07-10 13:04 +0100
                          Re: naked switches David Brown <david.brown@hesbynett.no> - 2021-07-10 15:39 +0200
                            Re: naked switches Bart <bc@freeuk.com> - 2021-07-10 15:08 +0100
                              Re: naked switches David Brown <david.brown@hesbynett.no> - 2021-07-10 17:34 +0200
                                Re: naked switches Bart <bc@freeuk.com> - 2021-07-10 17:47 +0100
                Re: naked switches Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-07-11 00:57 -0700
                  Re: naked switches Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-07-16 03:51 -0700
          Re: naked switches Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2021-07-08 11:40 -0700
            Re: naked switches David Brown <david.brown@hesbynett.no> - 2021-07-09 10:38 +0200
    Re: naked switches Kaz Kylheku <563-365-8930@kylheku.com> - 2021-07-08 17:33 +0000
      Re: naked switches scott@slp53.sl.home (Scott Lurndal) - 2021-07-08 17:47 +0000
        Re: naked switches Kaz Kylheku <563-365-8930@kylheku.com> - 2021-07-08 17:58 +0000
          Re: naked switches scott@slp53.sl.home (Scott Lurndal) - 2021-07-08 19:13 +0000
            Re: naked switches Kaz Kylheku <563-365-8930@kylheku.com> - 2021-07-09 02:30 +0000
          Re: naked switches David Brown <david.brown@hesbynett.no> - 2021-07-09 10:41 +0200
    Re: naked switches Bonita Montero <Bonita.Montero@gmail.com> - 2021-09-30 18:04 +0200

Page 1 of 3  [1] 2 3  Next page →


#161725 — naked switches

FromRobert Finch <robfi680@gmail.com>
Date2021-07-07 16:39 -0700
Subjectnaked switches
Message-ID<06a0049a-2d7d-40f0-899a-fb35c9a15d5fn@googlegroups.com>
I have been working on a C/C++ like compiler. One feature supported in the compiler is naked switches. A naked switch omits the range checking code that is normally associated with the switch statement. Omitting this code can improve performance at the risk of a crash if invalid cases are processed. I am wondering if there is a similar option in other C compilers? Or would this just be an automatic optimization at high levels?

Normal Switch:

  ;        	switch(x) {
  ldo      $t0,64[$fp]
  sge      $t1,$t0,#1		; x varies between 1 and 12
  sle      $t2,$t0,#12
  and      $t1,$t1,$t2
  beq      $t1,TestSwitch_89
  sub      $t0,$t0,#1
  sll      $t0,$t0,#4
  ldo      $t0,TestSwitch_116[$t0]
  jmp      $t0

Naked Switch
  ;        	switch(x; naked) {
  ldo      $t0,64[$fp]
  sub      $t0,$t0,#1
  sll      $t0,$t0,#4
  ldo      $t0,TestSwitch_144[$t0]
  jmp      $t0

[toc] | [next] | [standalone]


#161728

FromBart <bc@freeuk.com>
Date2021-07-08 01:26 +0100
Message-ID<sc5gnf$v05$1@dont-email.me>
In reply to#161725
On 08/07/2021 00:39, Robert Finch wrote:
> I have been working on a C/C++ like compiler. One feature supported in the compiler is naked switches. A naked switch omits the range checking code that is normally associated with the switch statement. Omitting this code can improve performance at the risk of a crash if invalid cases are processed. I am wondering if there is a similar option in other C compilers? Or would this just be an automatic optimization at high levels?
> 
> Normal Switch:
> 
>    ;        	switch(x) {
>    ldo      $t0,64[$fp]
>    sge      $t1,$t0,#1		; x varies between 1 and 12
>    sle      $t2,$t0,#12
>    and      $t1,$t1,$t2
>    beq      $t1,TestSwitch_89
>    sub      $t0,$t0,#1
>    sll      $t0,$t0,#4
>    ldo      $t0,TestSwitch_116[$t0]
>    jmp      $t0
> 
> Naked Switch
>    ;        	switch(x; naked) {
>    ldo      $t0,64[$fp]
>    sub      $t0,$t0,#1
>    sll      $t0,$t0,#4
>    ldo      $t0,TestSwitch_144[$t0]
>    jmp      $t0
> 

How much faster does this make it? Because I think that when I tried it 
on x64, it made no measurable difference. (Maybe the branch predictor 
dealt with it.)

(On x64 also, if there are fewer than about 8 jumptable entries, it was 
faster just to do sequential tests.)

Note that I do a single test, not two, after adjusting the index 
expression to be 0-based which is necessary as the jumptable is biased. 
So for your 1-12 range, I adjust to 0-11 and do an unsigned comparison 
with 11, which is two instructions.

As to what an optimising compiler could do, it could be anything at all, 
including optimising the whole switch out of existence.

[toc] | [prev] | [next] | [standalone]


#161733

FromKaz Kylheku <563-365-8930@kylheku.com>
Date2021-07-08 01:57 +0000
Message-ID<20210707175722.828@kylheku.com>
In reply to#161725
On 2021-07-07, Robert Finch <robfi680@gmail.com> wrote:
> I have been working on a C/C++ like compiler. One feature supported in
> the compiler is naked switches. A naked switch omits the range
> checking code that is normally associated with the switch statement.
> Omitting this code can improve performance at the risk of a crash if
> invalid cases are processed. I am wondering if there is a similar
> option in other C compilers? Or would this just be an automatic
> optimization at high levels?

I've not heard of such an option. Some modern compilers aggressively
optimize on the assumption that there is no undefined behavior.

Now, I will tell you a fantasy.

A conforming C implementation is free to assume that no undefined
behavior occurs because in that case no requirements apply.

For instance, if it is obvious that some pieces of code can only be
reached if the program has invoked undefined behavior, then that code
can be removed as if it were unreachable.  There is no requirement that
anything work at all after undefined behavior.

This concept allows you to encode assertions about properties like this:

  #define undefined_behavior (0/0)

  if (x < 1 || x > 12)
    perpetrate_undefined_behavior();

  switch (x)

Because undefined behavior happens if x is outside the range 1 to 12,
the assumption that the program is free of undefined behavior logically
implies that x is not outside of that range.

Therefore, the if statement can be optimized away, and the switch
statement.

A simpler way to express the above situation is this:

  switch (x) {
  // ... cases 1 to 12 ..
    break;
  // out of range cases:
  default: 0/0; // division by zero: undefined
  }

Basically we divert the out-of-range cases to an obvious, explicitly
defined undefined behavior. Then we hope that the compiler infers that,
since undefined does not happen, x can be assumed not to be outside of
the 1 to 12 range, and so why bother checking.

How well that works in practice, you have to determine experimentally.

Experimentally, I'm not able to get GCC to eliminate the check for x
being above 9 in this code:


  switch (x) {
  case 0: return "zero";
  case 1: return "one";
  case 2: return "two";
  case 3: return "three";
  case 4: return "four";
  case 5: return "five";
  case 6: return "six";
  case 7: return "seven";
  case 8: return "eight";
  case 9: return "nine";
  default: exit(x/0);
  }

The best that happens is that x is compared to 9, and if it is above,
a branch takes place to a label, where the "ud2" instruction is executed.
There is no division and no call to exit.

Basically, this "optimize based on defined behavior" business is the
programmer's enemy. It will not do the obvious optimizations you want,
but it will shoot you in the foot when you accidentally introduce some
undefined behavior, or use some formally undefined "classic" idiom that
has nevertheless worked on pretty much every compiler over forty yeras.

Now, GCC even has a way to explicitly assert "if we reach this point
in the program, the behavior is undefined". Even that is not getting rid
of the range test for me:

const char *fun(int x)
{
  switch (x) {
  case 0: return "zero";
  case 1: return "one";
  case 2: return "two";
  case 3: return "three";
  case 4: return "four";
  case 5: return "five";
  case 6: return "six";
  case 7: return "seven";
  case 8: return "eight";
  case 9: return "nine";
  default: __builtin_unreachable();
  }
}

Worse, gcc (version 7.5.0 on Ubuntu 18) is not emitting the ud2
instruction in this case, as it does for a null pointer dereference or
division by zero. It just generates code that returns from the function.

Maybe there is some option you're supposed to use for this, but I can't
find it.

[toc] | [prev] | [next] | [standalone]


#161738

FromDavid Brown <david.brown@hesbynett.no>
Date2021-07-08 09:16 +0200
Message-ID<sc68nr$ilo$1@dont-email.me>
In reply to#161733
On 08/07/2021 03:57, Kaz Kylheku wrote:
> On 2021-07-07, Robert Finch <robfi680@gmail.com> wrote:
>> I have been working on a C/C++ like compiler. One feature supported in
>> the compiler is naked switches. A naked switch omits the range
>> checking code that is normally associated with the switch statement.
>> Omitting this code can improve performance at the risk of a crash if
>> invalid cases are processed. I am wondering if there is a similar
>> option in other C compilers? Or would this just be an automatic
>> optimization at high levels?
> 
> I've not heard of such an option. 

Nor have I.  I'd suggest that it is a /really/ bad idea, since it
changes the semantics of the language.  It would be possible to have it
as a feature written in a way that screams "this is not a switch
statement as you know it", calling it "__naked_switch(...)" or
something.  But it would be simpler, clearer, safer, more portable and
more flexible to implement "__builtin_unreachable" as you suggest here.



> Some modern compilers aggressively
> optimize on the assumption that there is no undefined behavior.
> 
> Now, I will tell you a fantasy.
> 

It is no fantasy.  Some people really get their knickers in a twist
about it, but IMHO it is a good thing.

> A conforming C implementation is free to assume that no undefined
> behavior occurs because in that case no requirements apply.
> 
> For instance, if it is obvious that some pieces of code can only be
> reached if the program has invoked undefined behavior, then that code
> can be removed as if it were unreachable.  There is no requirement that
> anything work at all after undefined behavior.
> 
> This concept allows you to encode assertions about properties like this:
> 
>   #define undefined_behavior (0/0)
> 
>   if (x < 1 || x > 12)
>     perpetrate_undefined_behavior();
> 
>   switch (x)
> 
> Because undefined behavior happens if x is outside the range 1 to 12,
> the assumption that the program is free of undefined behavior logically
> implies that x is not outside of that range.
> 
> Therefore, the if statement can be optimized away, and the switch
> statement.
> 
> A simpler way to express the above situation is this:
> 
>   switch (x) {
>   // ... cases 1 to 12 ..
>     break;
>   // out of range cases:
>   default: 0/0; // division by zero: undefined
>   }
> 
> Basically we divert the out-of-range cases to an obvious, explicitly
> defined undefined behavior. Then we hope that the compiler infers that,
> since undefined does not happen, x can be assumed not to be outside of
> the 1 to 12 range, and so why bother checking.
> 
> How well that works in practice, you have to determine experimentally.
> 
> Experimentally, I'm not able to get GCC to eliminate the check for x
> being above 9 in this code:
> 
> 
>   switch (x) {
>   case 0: return "zero";
>   case 1: return "one";
>   case 2: return "two";
>   case 3: return "three";
>   case 4: return "four";
>   case 5: return "five";
>   case 6: return "six";
>   case 7: return "seven";
>   case 8: return "eight";
>   case 9: return "nine";
>   default: exit(x/0);
>   }
> 
> The best that happens is that x is compared to 9, and if it is above,
> a branch takes place to a label, where the "ud2" instruction is executed.
> There is no division and no call to exit.
> 

You might think "0 / 0" would be a good case for undefined behaviour
(and therefore "can't happen" optimisations), but it's not really.  On
many targets, actually executing "0 / 0" will cause a specific exception
or trap behaviour.  And while the C standards say nothing about what
will happen here, and call it "undefined behaviour", target-specific
details and implementation-specific details may vary.  The same applies
to anything else undefined in the C standards, such as dereferencing a
null pointer.

In addition, compilers often try to be helpful.  If it appears that you
have accidentally tried to do something impossible at run-time,
executing an implementation-specific "trap" instruction is a good choice
- it gives the developer a better chance of finding the problem and
limiting the damage than happily launching nasal demons.  So gcc
generates "ud2" instructions sometimes for x86, and similar instructions
for other processors.  (You can get this intentionally with
"__builtin_trap()".)

So the best choice is a compiler-specific explicit "undefined behaviour"
indicator, since there is no standard feature here.  For gcc and
compatible compilers, that is (as you know), __builtin_unreachable().


> Basically, this "optimize based on defined behavior" business is the
> programmer's enemy. It will not do the obvious optimizations you want,
> but it will shoot you in the foot when you accidentally introduce some
> undefined behavior, or use some formally undefined "classic" idiom that
> has nevertheless worked on pretty much every compiler over forty yeras.
> 
> Now, GCC even has a way to explicitly assert "if we reach this point
> in the program, the behavior is undefined". Even that is not getting rid
> of the range test for me:
> 
> const char *fun(int x)
> {
>   switch (x) {
>   case 0: return "zero";
>   case 1: return "one";
>   case 2: return "two";
>   case 3: return "three";
>   case 4: return "four";
>   case 5: return "five";
>   case 6: return "six";
>   case 7: return "seven";
>   case 8: return "eight";
>   case 9: return "nine";
>   default: __builtin_unreachable();
>   }
> }
> 
> Worse, gcc (version 7.5.0 on Ubuntu 18) is not emitting the ud2
> instruction in this case, as it does for a null pointer dereference or
> division by zero. It just generates code that returns from the function.
> 
> Maybe there is some option you're supposed to use for this, but I can't
> find it.
> 

The trick is to use gcc 8 or newer :-)

[toc] | [prev] | [next] | [standalone]


#161741

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2021-07-08 01:00 -0700
Message-ID<8735sp8cbm.fsf@nosuchdomain.example.com>
In reply to#161725
Robert Finch <robfi680@gmail.com> writes:
> I have been working on a C/C++ like compiler. One feature supported in
> the compiler is naked switches. A naked switch omits the range
> checking code that is normally associated with the switch
> statement. Omitting this code can improve performance at the risk of a
> crash if invalid cases are processed. I am wondering if there is a
> similar option in other C compilers? Or would this just be an
> automatic optimization at high levels?

What range checking code are you referring to?  Can you give an example
in C that demonstrates the change in behavior?  How does a default:
label affect it?

> Normal Switch:
>
>   ;        	switch(x) {
>   ldo      $t0,64[$fp]
>   sge      $t1,$t0,#1		; x varies between 1 and 12
>   sle      $t2,$t0,#12
>   and      $t1,$t1,$t2
>   beq      $t1,TestSwitch_89
>   sub      $t0,$t0,#1
>   sll      $t0,$t0,#4
>   ldo      $t0,TestSwitch_116[$t0]
>   jmp      $t0
>
> Naked Switch
>   ;        	switch(x; naked) {
>   ldo      $t0,64[$fp]
>   sub      $t0,$t0,#1
>   sll      $t0,$t0,#4
>   ldo      $t0,TestSwitch_144[$t0]
>   jmp      $t0
>

-- 
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */

[toc] | [prev] | [next] | [standalone]


#161748

FromMalcolm McLean <malcolm.arthur.mclean@gmail.com>
Date2021-07-08 03:25 -0700
Message-ID<255c09e7-2365-4983-ad4d-3bfc83cf04e7n@googlegroups.com>
In reply to#161741
On Thursday, 8 July 2021 at 09:00:40 UTC+1, Keith Thompson wrote:
> Robert Finch <robf...@gmail.com> writes: 
> > I have been working on a C/C++ like compiler. One feature supported in 
> > the compiler is naked switches. A naked switch omits the range 
> > checking code that is normally associated with the switch 
> > statement. Omitting this code can improve performance at the risk of a 
> > crash if invalid cases are processed. I am wondering if there is a 
> > similar option in other C compilers? Or would this just be an 
> > automatic optimization at high levels?
> What range checking code are you referring to? Can you give an example 
> in C that demonstrates the change in behavior? How does a default: 
>
Normal switch

foo(int x)
{
   switch(x)
   {
      case 1: printf("one\n"); break;
     case 2: printf("two\n"); break;
     case 3:: printf("three\n"); break;
     case 4: printf("four\n"): break;
     default: printf("x is in error\n"); break;
  }
}

That will compile to something like the following
if(x < 1 || x > 4) x -= 5;
x -= 1;
goto jumptable[x]

Namke switch

foo(int x)
{
    nakedswitch(x)
    {
       case 1: printf("one\n"); break;
     case 2: printf("two\n"); break;
     case 3:: printf("three\n"); break;
     case 4: printf("four\n"): break;
    }
}

would compile to the following
/* jumptable[0] = NULL; */
goto jumptable[x];

so x is unchecked. If it is out of range, the program crashes.

[toc] | [prev] | [next] | [standalone]


#161751

FromRobert Finch <robfi680@gmail.com>
Date2021-07-08 06:58 -0700
Message-ID<31342574-cb69-4ad6-8576-387dcf9caf70n@googlegroups.com>
In reply to#161748
On Thursday, July 8, 2021 at 6:25:26 AM UTC-4, Malcolm McLean wrote:
> On Thursday, 8 July 2021 at 09:00:40 UTC+1, Keith Thompson wrote: 
> > Robert Finch <robf...@gmail.com> writes: 
> > > I have been working on a C/C++ like compiler. One feature supported in 
> > > the compiler is naked switches. A naked switch omits the range 
> > > checking code that is normally associated with the switch 
> > > statement. Omitting this code can improve performance at the risk of a 
> > > crash if invalid cases are processed. I am wondering if there is a 
> > > similar option in other C compilers? Or would this just be an 
> > > automatic optimization at high levels? 
> > What range checking code are you referring to? Can you give an example 
> > in C that demonstrates the change in behavior? How does a default: 
> >
> Normal switch 
> 
> foo(int x) 
> { 
> switch(x) 
> { 
> case 1: printf("one\n"); break; 
> case 2: printf("two\n"); break; 
> case 3:: printf("three\n"); break; 
> case 4: printf("four\n"): break; 
> default: printf("x is in error\n"); break; 
> } 
> } 
> 
> That will compile to something like the following 
> if(x < 1 || x > 4) x -= 5; 
> x -= 1; 
> goto jumptable[x] 
> 
> Namke switch 
> 
> foo(int x) 
> { 
> nakedswitch(x) 
> { 
> case 1: printf("one\n"); break; 
> case 2: printf("two\n"); break; 
> case 3:: printf("three\n"); break; 
> case 4: printf("four\n"): break; 
> } 
> } 
> 
> would compile to the following 
> /* jumptable[0] = NULL; */ 
> goto jumptable[x]; 
> 
> so x is unchecked. If it is out of range, the program crashes.

That is basically how it is working. There is still a default statement for unimplemented values between the min and max. The table entry may as well point somewhere useful.
There were two goals with this, a) a performance optimization and b) code size optimization. I am dealing with small roms in an FPGA so bytes count. The processor is also rather slow <40MHz.

[toc] | [prev] | [next] | [standalone]


#161752

FromBart <bc@freeuk.com>
Date2021-07-08 15:52 +0100
Message-ID<sc73fs$g29$1@dont-email.me>
In reply to#161751
On 08/07/2021 14:58, Robert Finch wrote:
> On Thursday, July 8, 2021 at 6:25:26 AM UTC-4, Malcolm McLean wrote:
>> On Thursday, 8 July 2021 at 09:00:40 UTC+1, Keith Thompson wrote:
>>> Robert Finch <robf...@gmail.com> writes:
>>>> I have been working on a C/C++ like compiler. One feature supported in
>>>> the compiler is naked switches. A naked switch omits the range
>>>> checking code that is normally associated with the switch
>>>> statement. Omitting this code can improve performance at the risk of a
>>>> crash if invalid cases are processed. I am wondering if there is a
>>>> similar option in other C compilers? Or would this just be an
>>>> automatic optimization at high levels?
>>> What range checking code are you referring to? Can you give an example
>>> in C that demonstrates the change in behavior? How does a default:
>>>
>> Normal switch
>>
>> foo(int x)
>> {
>> switch(x)
>> {
>> case 1: printf("one\n"); break;
>> case 2: printf("two\n"); break;
>> case 3:: printf("three\n"); break;
>> case 4: printf("four\n"): break;
>> default: printf("x is in error\n"); break;
>> }
>> }
>>
>> That will compile to something like the following
>> if(x < 1 || x > 4) x -= 5;
>> x -= 1;
>> goto jumptable[x]
>>
>> Namke switch
>>
>> foo(int x)
>> {
>> nakedswitch(x)
>> {
>> case 1: printf("one\n"); break;
>> case 2: printf("two\n"); break;
>> case 3:: printf("three\n"); break;
>> case 4: printf("four\n"): break;
>> }
>> }
>>
>> would compile to the following
>> /* jumptable[0] = NULL; */
>> goto jumptable[x];
>>
>> so x is unchecked. If it is out of range, the program crashes.
> 
> That is basically how it is working. There is still a default statement for unimplemented values between the min and max. The table entry may as well point somewhere useful.
> There were two goals with this, a) a performance optimization and b) code size optimization. I am dealing with small roms in an FPGA so bytes count. The processor is also rather slow <40MHz.
> 

So, this just like array index bounds checking. A bounds check can be 
omitted if you're sure the index is within range.

Except that in C, bounds aren't checked anyway. However most /correct/ 
programs won't have out-of-bounds indices.

You can probably omit the switch bounds check if it makes a difference, 
if you are equally sure the index will be within range.

If not, then it might be best left in. Although you can try the single 
unsigned comparison if the hardware allows.

[toc] | [prev] | [next] | [standalone]


#161753

FromDavid Brown <david.brown@hesbynett.no>
Date2021-07-08 17:52 +0200
Message-ID<sc76v8$8ah$1@dont-email.me>
In reply to#161751
On 08/07/2021 15:58, Robert Finch wrote:
> On Thursday, July 8, 2021 at 6:25:26 AM UTC-4, Malcolm McLean wrote:
>> On Thursday, 8 July 2021 at 09:00:40 UTC+1, Keith Thompson wrote: 
>>> Robert Finch <robf...@gmail.com> writes: 
>>>> I have been working on a C/C++ like compiler. One feature supported in 
>>>> the compiler is naked switches. A naked switch omits the range 
>>>> checking code that is normally associated with the switch 
>>>> statement. Omitting this code can improve performance at the risk of a 
>>>> crash if invalid cases are processed. I am wondering if there is a 
>>>> similar option in other C compilers? Or would this just be an 
>>>> automatic optimization at high levels? 


> 
> That is basically how it is working. There is still a default
> statement for unimplemented values between the min and max. The table
> entry may as well point somewhere useful. There were two goals with
> this, a) a performance optimization and b) code size optimization. I
> am dealing with small roms in an FPGA so bytes count. The processor
> is also rather slow <40MHz.
> 
> 

You wrote that you "have been working on a C/C++ like compiler" - do you
mean you have been /using/ such a compiler, or you have been /writing/
such a compiler?

As I mentioned earlier, I think a "naked switch" like this is a terrible
idea.  It is not something I have seen on other compilers, and I've used
quite a large number over the years for far smaller and slower devices
than you are describing here.

The way you handle this with gcc has already been covered - you use
__builtin_unreachable() to tell the compiler how to optimise for "this
can't happen" cases.  clang supports __builtin_unreachable() too, and
MSVC has "__assume(false)" that has the same effect.

[toc] | [prev] | [next] | [standalone]


#161755

FromBart <bc@freeuk.com>
Date2021-07-08 17:51 +0100
Message-ID<sc7aeg$10u$1@dont-email.me>
In reply to#161753
On 08/07/2021 16:52, David Brown wrote:
> On 08/07/2021 15:58, Robert Finch wrote:
>> On Thursday, July 8, 2021 at 6:25:26 AM UTC-4, Malcolm McLean wrote:
>>> On Thursday, 8 July 2021 at 09:00:40 UTC+1, Keith Thompson wrote:
>>>> Robert Finch <robf...@gmail.com> writes:
>>>>> I have been working on a C/C++ like compiler. One feature supported in
>>>>> the compiler is naked switches. A naked switch omits the range
>>>>> checking code that is normally associated with the switch
>>>>> statement. Omitting this code can improve performance at the risk of a
>>>>> crash if invalid cases are processed. I am wondering if there is a
>>>>> similar option in other C compilers? Or would this just be an
>>>>> automatic optimization at high levels?
> 
> 
>>
>> That is basically how it is working. There is still a default
>> statement for unimplemented values between the min and max. The table
>> entry may as well point somewhere useful. There were two goals with
>> this, a) a performance optimization and b) code size optimization. I
>> am dealing with small roms in an FPGA so bytes count. The processor
>> is also rather slow <40MHz.
>>
>>
> 
> You wrote that you "have been working on a C/C++ like compiler" - do you
> mean you have been /using/ such a compiler, or you have been /writing/
> such a compiler?
> 
> As I mentioned earlier, I think a "naked switch" like this is a terrible
> idea.

I've considered having something like that. I'd have called it 'uswitch'.

It was never done because:

* The range check wasn't really much of an overhead on x64 (the indexed 
jump is)

* I could never be sure that the switch index would always be inside the 
range of the minimum and maximum values of the switch cases

* It seemed a bit naff

But I wouldn't find it objectionable if it helps out on a slower 
processor. After all array indices are not checked either as I said in 
my last post.

   It is not something I have seen on other compilers, and I've used
> quite a large number over the years for far smaller and slower devices
> than you are describing here.
> 
> The way you handle this with gcc has already been covered - you use
> __builtin_unreachable() to tell the compiler how to optimise for "this
> can't happen" cases.  clang supports __builtin_unreachable() too, and
> MSVC has "__assume(false)" that has the same effect.
> 

How would that work in that case? There doesn't appear to be a bit of 
code to hang that onto, unless you specifically create an empty block 
for the purpose, guarded by the same sort of range check you want to avoid.

If you mean putting it on the default: case of the switch, then that is 
not the same thing: if you know your switch index values are in the 
range 1 to 5 include, you may have case labels for 1, 3 and 5, and want 
default handling for 2 and 4, which /is/ reachable.

You just don't want the unnecessary check for being within 1 to 5.

[toc] | [prev] | [next] | [standalone]


#161773

FromDavid Brown <david.brown@hesbynett.no>
Date2021-07-09 10:01 +0200
Message-ID<sc8von$8fo$1@dont-email.me>
In reply to#161755
On 08/07/2021 18:51, Bart wrote:
> On 08/07/2021 16:52, David Brown wrote:
>> On 08/07/2021 15:58, Robert Finch wrote:
>>> On Thursday, July 8, 2021 at 6:25:26 AM UTC-4, Malcolm McLean wrote:
>>>> On Thursday, 8 July 2021 at 09:00:40 UTC+1, Keith Thompson wrote:
>>>>> Robert Finch <robf...@gmail.com> writes:
>>>>>> I have been working on a C/C++ like compiler. One feature
>>>>>> supported in
>>>>>> the compiler is naked switches. A naked switch omits the range
>>>>>> checking code that is normally associated with the switch
>>>>>> statement. Omitting this code can improve performance at the risk
>>>>>> of a
>>>>>> crash if invalid cases are processed. I am wondering if there is a
>>>>>> similar option in other C compilers? Or would this just be an
>>>>>> automatic optimization at high levels?
>>
>>
>>>
>>> That is basically how it is working. There is still a default
>>> statement for unimplemented values between the min and max. The table
>>> entry may as well point somewhere useful. There were two goals with
>>> this, a) a performance optimization and b) code size optimization. I
>>> am dealing with small roms in an FPGA so bytes count. The processor
>>> is also rather slow <40MHz.
>>>
>>>
>>
>> You wrote that you "have been working on a C/C++ like compiler" - do you
>> mean you have been /using/ such a compiler, or you have been /writing/
>> such a compiler?
>>
>> As I mentioned earlier, I think a "naked switch" like this is a terrible
>> idea.
> 
> I've considered having something like that. I'd have called it 'uswitch'.
> 
> It was never done because:
> 
> * The range check wasn't really much of an overhead on x64 (the indexed
> jump is)
> 
> * I could never be sure that the switch index would always be inside the
> range of the minimum and maximum values of the switch cases
> 
> * It seemed a bit naff
> 
> But I wouldn't find it objectionable if it helps out on a slower
> processor. After all array indices are not checked either as I said in
> my last post.
> 

The critical difference is the semantics of the C language - array
indexes are not checked automatically in C, the range in a switch /is/
checked because a switch is defined in the language to do nothing if the
value does not match any of the cases.

If you are making a different language, you can pick different rules.

I've seen many compilers that have odd non-standard behaviour "to make C
simpler" or "to get better code on this little processor".  IME, it is
/always/ a mistake.  I've seen "const" used to mean "this is in flash",
changes to the integer promotion rules, and other "improvements".  The
result is always mixups and misunderstandings, incompatibilities and
people writing poorer code that is harder to follow, less portable /and/
gives less efficient results on the smart-arse toolchain.

If you are making a C compiler, make a /C/ compiler.  If you want to get
better results, make your optimisation smarter.  If you want to give
users something extra to squeeze a little more out of the target, give
them something /useful/, /clear/, and /optional/.  The answer here is
__builtin_unreachable(), or __assume if you prefer MSVC's solution.

>   It is not something I have seen on other compilers, and I've used
>> quite a large number over the years for far smaller and slower devices
>> than you are describing here.
>>
>> The way you handle this with gcc has already been covered - you use
>> __builtin_unreachable() to tell the compiler how to optimise for "this
>> can't happen" cases.  clang supports __builtin_unreachable() too, and
>> MSVC has "__assume(false)" that has the same effect.
>>
> 
> How would that work in that case? There doesn't appear to be a bit of
> code to hang that onto, unless you specifically create an empty block
> for the purpose, guarded by the same sort of range check you want to avoid.

The normal situation would be :

	switch (x) {
		case 1 : handle1(); break;
		case 2 : handle2(); break;
		case 4 : handle4(); break;
		default : __builtin_unreachable();	// gcc
		default : __assume(0);			// msvc
	}



If you want to say x == 3 is a "do nothing" situation, but you want to
tell the compiler it doesn't need to check for x < 1 or x > 4, then you
do so simply and clearly:

	if ((x < 1) || (x > 4)) __builtin_unreachable();	// gcc
	__assume((x >= 1) && (x <= 4));				// msvc

	switch (x) {
		case 1 : handle1(); break;
		case 2 : handle2(); break;
		case 4 : handle4(); break;
	}


Compare that to someone looking at "uswitch" and wondering what that
might possibly mean, or trying to guess if it is safe to pass 3 as x, or
figuring out if the code is correct and efficient when you have the
cases in a different order.

Once you have __builtin_unreachable() or __assume(), you have a tool
that can be re-used in all sorts of other situations instead of just one
minor use-case with little impact.  And this tool is useful for giving
the compiler more information, and for documenting the programmer's
intentions and assumptions.

You can also use a macro or inline function here, along the lines of :

#if DEBUGMODE
	#define impossible() send_bug_report_to_programmer()
#else
	#define impossible() __builtin_unreachable()
#endif

Now you have a combined debugging tool, documentation tool, and more
efficient code.


> 
> If you mean putting it on the default: case of the switch, then that is
> not the same thing: if you know your switch index values are in the
> range 1 to 5 include, you may have case labels for 1, 3 and 5, and want
> default handling for 2 and 4, which /is/ reachable.
> 
> You just don't want the unnecessary check for being within 1 to 5.

Put the check where you want the check to happen.

[toc] | [prev] | [next] | [standalone]


#161777

FromBart <bc@freeuk.com>
Date2021-07-09 11:09 +0100
Message-ID<sc979l$n65$1@dont-email.me>
In reply to#161773
On 09/07/2021 09:01, David Brown wrote:
> On 08/07/2021 18:51, Bart wrote:
>> On 08/07/2021 16:52, David Brown wrote:
>>> On 08/07/2021 15:58, Robert Finch wrote:
>>>> On Thursday, July 8, 2021 at 6:25:26 AM UTC-4, Malcolm McLean wrote:
>>>>> On Thursday, 8 July 2021 at 09:00:40 UTC+1, Keith Thompson wrote:
>>>>>> Robert Finch <robf...@gmail.com> writes:
>>>>>>> I have been working on a C/C++ like compiler. One feature
>>>>>>> supported in
>>>>>>> the compiler is naked switches. A naked switch omits the range
>>>>>>> checking code that is normally associated with the switch
>>>>>>> statement. Omitting this code can improve performance at the risk
>>>>>>> of a
>>>>>>> crash if invalid cases are processed. I am wondering if there is a
>>>>>>> similar option in other C compilers? Or would this just be an
>>>>>>> automatic optimization at high levels?
>>>
>>>
>>>>
>>>> That is basically how it is working. There is still a default
>>>> statement for unimplemented values between the min and max. The table
>>>> entry may as well point somewhere useful. There were two goals with
>>>> this, a) a performance optimization and b) code size optimization. I
>>>> am dealing with small roms in an FPGA so bytes count. The processor
>>>> is also rather slow <40MHz.
>>>>
>>>>
>>>
>>> You wrote that you "have been working on a C/C++ like compiler" - do you
>>> mean you have been /using/ such a compiler, or you have been /writing/
>>> such a compiler?
>>>
>>> As I mentioned earlier, I think a "naked switch" like this is a terrible
>>> idea.
>>
>> I've considered having something like that. I'd have called it 'uswitch'.
>>
>> It was never done because:
>>
>> * The range check wasn't really much of an overhead on x64 (the indexed
>> jump is)
>>
>> * I could never be sure that the switch index would always be inside the
>> range of the minimum and maximum values of the switch cases
>>
>> * It seemed a bit naff
>>
>> But I wouldn't find it objectionable if it helps out on a slower
>> processor. After all array indices are not checked either as I said in
>> my last post.
>>
> 
> The critical difference is the semantics of the C language - array
> indexes are not checked automatically in C, the range in a switch /is/
> checked because a switch is defined in the language to do nothing if the
> value does not match any of the cases.
> 
> If you are making a different language, you can pick different rules.
> 
> I've seen many compilers that have odd non-standard behaviour "to make C
> simpler" or "to get better code on this little processor".  IME, it is
> /always/ a mistake.  I've seen "const" used to mean "this is in flash",
> changes to the integer promotion rules, and other "improvements".  The
> result is always mixups and misunderstandings, incompatibilities and
> people writing poorer code that is harder to follow, less portable /and/
> gives less efficient results on the smart-arse toolchain.
> 
> If you are making a C compiler, make a /C/ compiler.  If you want to get
> better results, make your optimisation smarter.  If you want to give
> users something extra to squeeze a little more out of the target, give
> them something /useful/, /clear/, and /optional/.  The answer here is
> __builtin_unreachable(), or __assume if you prefer MSVC's solution.
> 
>>    It is not something I have seen on other compilers, and I've used
>>> quite a large number over the years for far smaller and slower devices
>>> than you are describing here.
>>>
>>> The way you handle this with gcc has already been covered - you use
>>> __builtin_unreachable() to tell the compiler how to optimise for "this
>>> can't happen" cases.  clang supports __builtin_unreachable() too, and
>>> MSVC has "__assume(false)" that has the same effect.
>>>
>>
>> How would that work in that case? There doesn't appear to be a bit of
>> code to hang that onto, unless you specifically create an empty block
>> for the purpose, guarded by the same sort of range check you want to avoid.
> 
> The normal situation would be :
> 
> 	switch (x) {
> 		case 1 : handle1(); break;
> 		case 2 : handle2(); break;
> 		case 4 : handle4(); break;
> 		default : __builtin_unreachable();	// gcc
> 		default : __assume(0);			// msvc
> 	}

As I said, you may want to reserve default: for x==3.

> 
> 
> 
> If you want to say x == 3 is a "do nothing" situation, but you want to
> tell the compiler it doesn't need to check for x < 1 or x > 4, then you
> do so simply and clearly:
> 
> 	if ((x < 1) || (x > 4)) __builtin_unreachable();	// gcc
> 	__assume((x >= 1) && (x <= 4));				// msvc

This is really ugly and may involve some compilers (eg. mine) adding all 
those extra checks.

But also, how would this work in practice? Case values are usually 
enums, you'd need to go and find which are the minimum and maximum 
enums, and hope they don't change.

However, if you know that x is always going to have a value of one of 
those enums, and all enum cases are checked (no gaps), then uswitch will 
be safe.

Gaps could be allowed, except for the danger that the gaps could be at 
either end (so for enums of 1,2,3,4,5, it may omit 1 and/or 5, but it 
will then assume 2-5 or 1-4).

> 
> 	switch (x) {
> 		case 1 : handle1(); break;
> 		case 2 : handle2(); break;
> 		case 4 : handle4(); break;
> 	}
> 
> 
> Compare that to someone looking at "uswitch" and wondering what that
> might possibly mean,


Unchecked or unsafe. But no worse than scratching their head over 
__builtin_unreachable and wondering what it has to do with that switch 
further down the function.

[toc] | [prev] | [next] | [standalone]


#161779

FromDavid Brown <david.brown@hesbynett.no>
Date2021-07-09 13:14 +0200
Message-ID<sc9b35$g6s$1@dont-email.me>
In reply to#161777
On 09/07/2021 12:09, Bart wrote:
> On 09/07/2021 09:01, David Brown wrote:
>> On 08/07/2021 18:51, Bart wrote:
>>> On 08/07/2021 16:52, David Brown wrote:
>>>> On 08/07/2021 15:58, Robert Finch wrote:
>>>>> On Thursday, July 8, 2021 at 6:25:26 AM UTC-4, Malcolm McLean wrote:
>>>>>> On Thursday, 8 July 2021 at 09:00:40 UTC+1, Keith Thompson wrote:
>>>>>>> Robert Finch <robf...@gmail.com> writes:
>>>>>>>> I have been working on a C/C++ like compiler. One feature
>>>>>>>> supported in
>>>>>>>> the compiler is naked switches. A naked switch omits the range
>>>>>>>> checking code that is normally associated with the switch
>>>>>>>> statement. Omitting this code can improve performance at the risk
>>>>>>>> of a
>>>>>>>> crash if invalid cases are processed. I am wondering if there is a
>>>>>>>> similar option in other C compilers? Or would this just be an
>>>>>>>> automatic optimization at high levels?
>>>>
>>>>
>>>>>
>>>>> That is basically how it is working. There is still a default
>>>>> statement for unimplemented values between the min and max. The table
>>>>> entry may as well point somewhere useful. There were two goals with
>>>>> this, a) a performance optimization and b) code size optimization. I
>>>>> am dealing with small roms in an FPGA so bytes count. The processor
>>>>> is also rather slow <40MHz.
>>>>>
>>>>>
>>>>
>>>> You wrote that you "have been working on a C/C++ like compiler" - do
>>>> you
>>>> mean you have been /using/ such a compiler, or you have been /writing/
>>>> such a compiler?
>>>>
>>>> As I mentioned earlier, I think a "naked switch" like this is a
>>>> terrible
>>>> idea.
>>>
>>> I've considered having something like that. I'd have called it
>>> 'uswitch'.
>>>
>>> It was never done because:
>>>
>>> * The range check wasn't really much of an overhead on x64 (the indexed
>>> jump is)
>>>
>>> * I could never be sure that the switch index would always be inside the
>>> range of the minimum and maximum values of the switch cases
>>>
>>> * It seemed a bit naff
>>>
>>> But I wouldn't find it objectionable if it helps out on a slower
>>> processor. After all array indices are not checked either as I said in
>>> my last post.
>>>
>>
>> The critical difference is the semantics of the C language - array
>> indexes are not checked automatically in C, the range in a switch /is/
>> checked because a switch is defined in the language to do nothing if the
>> value does not match any of the cases.
>>
>> If you are making a different language, you can pick different rules.
>>
>> I've seen many compilers that have odd non-standard behaviour "to make C
>> simpler" or "to get better code on this little processor".  IME, it is
>> /always/ a mistake.  I've seen "const" used to mean "this is in flash",
>> changes to the integer promotion rules, and other "improvements".  The
>> result is always mixups and misunderstandings, incompatibilities and
>> people writing poorer code that is harder to follow, less portable /and/
>> gives less efficient results on the smart-arse toolchain.
>>
>> If you are making a C compiler, make a /C/ compiler.  If you want to get
>> better results, make your optimisation smarter.  If you want to give
>> users something extra to squeeze a little more out of the target, give
>> them something /useful/, /clear/, and /optional/.  The answer here is
>> __builtin_unreachable(), or __assume if you prefer MSVC's solution.
>>
>>>    It is not something I have seen on other compilers, and I've used
>>>> quite a large number over the years for far smaller and slower devices
>>>> than you are describing here.
>>>>
>>>> The way you handle this with gcc has already been covered - you use
>>>> __builtin_unreachable() to tell the compiler how to optimise for "this
>>>> can't happen" cases.  clang supports __builtin_unreachable() too, and
>>>> MSVC has "__assume(false)" that has the same effect.
>>>>
>>>
>>> How would that work in that case? There doesn't appear to be a bit of
>>> code to hang that onto, unless you specifically create an empty block
>>> for the purpose, guarded by the same sort of range check you want to
>>> avoid.
>>
>> The normal situation would be :
>>
>>     switch (x) {
>>         case 1 : handle1(); break;
>>         case 2 : handle2(); break;
>>         case 4 : handle4(); break;
>>         default : __builtin_unreachable();    // gcc
>>         default : __assume(0);            // msvc
>>     }
> 
> As I said, you may want to reserve default: for x==3.

See below.

> 
>>
>>
>>
>> If you want to say x == 3 is a "do nothing" situation, but you want to
>> tell the compiler it doesn't need to check for x < 1 or x > 4, then you
>> do so simply and clearly:
>>
>>     if ((x < 1) || (x > 4)) __builtin_unreachable();    // gcc
>>     __assume((x >= 1) && (x <= 4));                // msvc
> 
> This is really ugly and may involve some compilers (eg. mine) adding all
> those extra checks.

I am not overly interested in such limited compilers.  It is silly to
worry about the cost of an extra comparison or two in a normal switch
for a compiler (or compiler options) that can't even eliminate such
extra comparisons.

If you think the double underlines are ugly (and I won't disagree), use
a macro to give it a nicer name.  Extensions are given such names to
avoid conflicts with valid user code.  (Okay, it's unlikely that someone
would use "builtin_unreachable" as an identifier - but they certainly
could use "assume" or other short and neat alternatives.)

> 
> But also, how would this work in practice? Case values are usually
> enums, you'd need to go and find which are the minimum and maximum
> enums, and hope they don't change.

You would not do that - because it would be a silly thing to do!  There
really is no use-case for saying "This variable should be one of these
following values.  If it is not, but it lies between the smallest and
the largest of these cases, then do nothing.  But if it is outside that
range, do whatever you want."  In reality, when you have a switch for an
enumeration type, you either want clear guaranteed behaviour for
unspecified cases (i.e., a "default" clause or "do nothing), or you are
confident that you will never run that code with an invalid enumeration
value, in which case "default : __builtin_unreachable();" is your answer.

Then it is up to the compiler to figure out how to handle the switch and
generate the best code - jump tables, calculations, if-then-else trees,
or whatever.

(A compiler warning for a switch of an enumeration type which does not
cover all enumeration values is a very useful help.)

> 
> However, if you know that x is always going to have a value of one of
> those enums, and all enum cases are checked (no gaps), then uswitch will
> be safe.
> 

True.

But "default : __builtin_unreachable();" is clearer, more flexible, more
portable, avoids an extra statement extension, and is a tool that can be
used in far more situations.

> Gaps could be allowed, except for the danger that the gaps could be at
> either end (so for enums of 1,2,3,4,5, it may omit 1 and/or 5, but it
> will then assume 2-5 or 1-4).
> 
>>
>>     switch (x) {
>>         case 1 : handle1(); break;
>>         case 2 : handle2(); break;
>>         case 4 : handle4(); break;
>>     }
>>
>>
>> Compare that to someone looking at "uswitch" and wondering what that
>> might possibly mean,
> 
> 
> Unchecked or unsafe. But no worse than scratching their head over
> __builtin_unreachable and wondering what it has to do with that switch
> further down the function.
> 

You are making up excuses.  I don't believe you would have trouble
guessing what "__builtin_unreachable()" does - unlike "uswitch" or
"naked_switch".

[toc] | [prev] | [next] | [standalone]


#161784

FromManfred <noname@add.invalid>
Date2021-07-09 15:35 +0200
Message-ID<sc9jam$1ahh$1@gioia.aioe.org>
In reply to#161777
On 7/9/2021 12:09 PM, Bart wrote:
> On 09/07/2021 09:01, David Brown wrote:
>> On 08/07/2021 18:51, Bart wrote:
>>> On 08/07/2021 16:52, David Brown wrote:
[...]
>>>    It is not something I have seen on other compilers, and I've used
>>>> quite a large number over the years for far smaller and slower devices
>>>> than you are describing here.
>>>>
>>>> The way you handle this with gcc has already been covered - you use
>>>> __builtin_unreachable() to tell the compiler how to optimise for "this
>>>> can't happen" cases.  clang supports __builtin_unreachable() too, and
>>>> MSVC has "__assume(false)" that has the same effect.
>>>>
>>>
>>> How would that work in that case? There doesn't appear to be a bit of
>>> code to hang that onto, unless you specifically create an empty block
>>> for the purpose, guarded by the same sort of range check you want to 
>>> avoid.
>>
>> The normal situation would be :
>>
>>     switch (x) {
>>         case 1 : handle1(); break;
>>         case 2 : handle2(); break;
>>         case 4 : handle4(); break;
>>         default : __builtin_unreachable();    // gcc
>>         default : __assume(0);            // msvc
>>     }
> 
> As I said, you may want to reserve default: for x==3.
> 
>>
>>
>>
>> If you want to say x == 3 is a "do nothing" situation, but you want to
>> tell the compiler it doesn't need to check for x < 1 or x > 4, then you
>> do so simply and clearly:
>>
>>     if ((x < 1) || (x > 4)) __builtin_unreachable();    // gcc
>>     __assume((x >= 1) && (x <= 4));                // msvc
> 
> This is really ugly and may involve some compilers (eg. mine) adding all 
> those extra checks.
> 

__builtin_unreachable() is a GCC extension (and similarly __assume() for 
msvc) that is supposed to have no meaning other than for GCC. And that 
meaning is an instruction for the compiler to consider that range for 
'x' impossible to happen, and optimize accordingly - not to add any 
extra check.
The fact that your compiler may interpret this instruction differently 
is irrelevant because, well, this is a GCC-specific extension - in fact 
your compiler should just /refuse/ to compile the code because 
__builtin_unreachable() should be undefined in your implementation.

Ugliness is an intentional feature of implementation-specific extensions.

> But also, how would this work in practice? Case values are usually 
> enums, you'd need to go and find which are the minimum and maximum 
> enums, and hope they don't change.
> 
> However, if you know that x is always going to have a value of one of 
> those enums, and all enum cases are checked (no gaps), then uswitch will 
> be safe.
> 
> Gaps could be allowed, except for the danger that the gaps could be at 
> either end (so for enums of 1,2,3,4,5, it may omit 1 and/or 5, but it 
> will then assume 2-5 or 1-4).
> 

Extensions of this kind, that go beyond the scope of the core language, 
are not required to feature compact and elegant code - these are 
requirements for the core language. I think it is OK to have to get 
through some coding gymnastics if you want to interact with 
implementation deepnesses of your specific compiler.
If you use them a lot in your current project, macros are the usual 
solution of course.

>>
>>     switch (x) {
>>         case 1 : handle1(); break;
>>         case 2 : handle2(); break;
>>         case 4 : handle4(); break;
>>     }
>>
>>
>> Compare that to someone looking at "uswitch" and wondering what that
>> might possibly mean,
> 
> 
> Unchecked or unsafe. But no worse than scratching their head over 
> __builtin_unreachable and wondering what it has to do with that switch 
> further down the function.
> 

The topic is about a customized variant of some language construct, for 
which you need specific compiler support - whatever compiler you use, 
you need to know it in detail, and __builtin_unreachable is part of the 
game if you use GCC.

[toc] | [prev] | [next] | [standalone]


#161788

FromDavid Brown <david.brown@hesbynett.no>
Date2021-07-09 16:51 +0200
Message-ID<sc9npu$9t3$1@dont-email.me>
In reply to#161784
On 09/07/2021 15:35, Manfred wrote:
> On 7/9/2021 12:09 PM, Bart wrote:
>> On 09/07/2021 09:01, David Brown wrote:
>>> On 08/07/2021 18:51, Bart wrote:
>>>> On 08/07/2021 16:52, David Brown wrote:
> [...]
>>>>    It is not something I have seen on other compilers, and I've used
>>>>> quite a large number over the years for far smaller and slower devices
>>>>> than you are describing here.
>>>>>
>>>>> The way you handle this with gcc has already been covered - you use
>>>>> __builtin_unreachable() to tell the compiler how to optimise for "this
>>>>> can't happen" cases.  clang supports __builtin_unreachable() too, and
>>>>> MSVC has "__assume(false)" that has the same effect.
>>>>>
>>>>
>>>> How would that work in that case? There doesn't appear to be a bit of
>>>> code to hang that onto, unless you specifically create an empty block
>>>> for the purpose, guarded by the same sort of range check you want to
>>>> avoid.
>>>
>>> The normal situation would be :
>>>
>>>     switch (x) {
>>>         case 1 : handle1(); break;
>>>         case 2 : handle2(); break;
>>>         case 4 : handle4(); break;
>>>         default : __builtin_unreachable();    // gcc
>>>         default : __assume(0);            // msvc
>>>     }
>>
>> As I said, you may want to reserve default: for x==3.
>>
>>>
>>>
>>>
>>> If you want to say x == 3 is a "do nothing" situation, but you want to
>>> tell the compiler it doesn't need to check for x < 1 or x > 4, then you
>>> do so simply and clearly:
>>>
>>>     if ((x < 1) || (x > 4)) __builtin_unreachable();    // gcc
>>>     __assume((x >= 1) && (x <= 4));                // msvc
>>
>> This is really ugly and may involve some compilers (eg. mine) adding
>> all those extra checks.
>>
> 
> __builtin_unreachable() is a GCC extension (and similarly __assume() for
> msvc) that is supposed to have no meaning other than for GCC. And that
> meaning is an instruction for the compiler to consider that range for
> 'x' impossible to happen, and optimize accordingly - not to add any
> extra check.
> The fact that your compiler may interpret this instruction differently
> is irrelevant because, well, this is a GCC-specific extension - in fact
> your compiler should just /refuse/ to compile the code because
> __builtin_unreachable() should be undefined in your implementation.
> 

Presumably a compiler that does not implement __builtin_unreachable() as
an extension will see it as a call to a function.  If the compiler is
C90, rather than C99, then it will have to accept the code even though
the function is not declared, and it will treat it as a normal function
- thus it will include the range check on x, and a call to an external
function "__builtin_unreachable()".  This is, of course, not remotely
close to what you want to happen.

If you are using features like this and want your code to be portable,
you want something like :

#ifdef __GNUC__
	#define Unreachable() __builtin_unreachable()
#elif defined(_MSC_VER)
	#define Unreachable() __assume(0)
#else
	#define Unreachable()
#endif

Then use Unreachable() macro in your code.

(And if the compiler still generates the extra range checks, then stop
worrying about the insignificant inefficiencies in the switch statement,
as you are not using an optimising compiler.)


> Ugliness is an intentional feature of implementation-specific extensions.
> 

[toc] | [prev] | [next] | [standalone]


#161792

FromManfred <noname@add.invalid>
Date2021-07-09 18:23 +0200
Message-ID<sc9t6i$1vbt$1@gioia.aioe.org>
In reply to#161788
On 7/9/2021 4:51 PM, David Brown wrote:
> On 09/07/2021 15:35, Manfred wrote:
>> On 7/9/2021 12:09 PM, Bart wrote:
>>> On 09/07/2021 09:01, David Brown wrote:
>>>> On 08/07/2021 18:51, Bart wrote:
>>>>> On 08/07/2021 16:52, David Brown wrote:
>> [...]
>>>>>     It is not something I have seen on other compilers, and I've used
>>>>>> quite a large number over the years for far smaller and slower devices
>>>>>> than you are describing here.
>>>>>>
>>>>>> The way you handle this with gcc has already been covered - you use
>>>>>> __builtin_unreachable() to tell the compiler how to optimise for "this
>>>>>> can't happen" cases.  clang supports __builtin_unreachable() too, and
>>>>>> MSVC has "__assume(false)" that has the same effect.
>>>>>>
>>>>>
>>>>> How would that work in that case? There doesn't appear to be a bit of
>>>>> code to hang that onto, unless you specifically create an empty block
>>>>> for the purpose, guarded by the same sort of range check you want to
>>>>> avoid.
>>>>
>>>> The normal situation would be :
>>>>
>>>>      switch (x) {
>>>>          case 1 : handle1(); break;
>>>>          case 2 : handle2(); break;
>>>>          case 4 : handle4(); break;
>>>>          default : __builtin_unreachable();    // gcc
>>>>          default : __assume(0);            // msvc
>>>>      }
>>>
>>> As I said, you may want to reserve default: for x==3.
>>>
>>>>
>>>>
>>>>
>>>> If you want to say x == 3 is a "do nothing" situation, but you want to
>>>> tell the compiler it doesn't need to check for x < 1 or x > 4, then you
>>>> do so simply and clearly:
>>>>
>>>>      if ((x < 1) || (x > 4)) __builtin_unreachable();    // gcc
>>>>      __assume((x >= 1) && (x <= 4));                // msvc
>>>
>>> This is really ugly and may involve some compilers (eg. mine) adding
>>> all those extra checks.
>>>
>>
>> __builtin_unreachable() is a GCC extension (and similarly __assume() for
>> msvc) that is supposed to have no meaning other than for GCC. And that
>> meaning is an instruction for the compiler to consider that range for
>> 'x' impossible to happen, and optimize accordingly - not to add any
>> extra check.
>> The fact that your compiler may interpret this instruction differently
>> is irrelevant because, well, this is a GCC-specific extension - in fact
>> your compiler should just /refuse/ to compile the code because
>> __builtin_unreachable() should be undefined in your implementation.
>>
> 
> Presumably a compiler that does not implement __builtin_unreachable() as
> an extension will see it as a call to a function.  If the compiler is
> C90, rather than C99, then it will have to accept the code even though
> the function is not declared, and it will treat it as a normal function
> - thus it will include the range check on x, and a call to an external
> function "__builtin_unreachable()".  This is, of course, not remotely
> close to what you want to happen.

The compiler may generate the call, but the linker is not going to 
assemble an executable if the function does not exist. This still holds 
in C90.

> 
> If you are using features like this and want your code to be portable,
> you want something like :
> 
> #ifdef __GNUC__
> 	#define Unreachable() __builtin_unreachable()
> #elif defined(_MSC_VER)
> 	#define Unreachable() __assume(0)
> #else
> 	#define Unreachable()
> #endif
> 
> Then use Unreachable() macro in your code.
> 
> (And if the compiler still generates the extra range checks, then stop
> worrying about the insignificant inefficiencies in the switch statement,
> as you are not using an optimising compiler.)
> 
> 
>> Ugliness is an intentional feature of implementation-specific extensions.
>>
> 

[toc] | [prev] | [next] | [standalone]


#161789

FromBart <bc@freeuk.com>
Date2021-07-09 16:07 +0100
Message-ID<sc9oni$go9$1@dont-email.me>
In reply to#161784
On 09/07/2021 14:35, Manfred wrote:
> On 7/9/2021 12:09 PM, Bart wrote:
>> On 09/07/2021 09:01, David Brown wrote:
>>> On 08/07/2021 18:51, Bart wrote:
>>>> On 08/07/2021 16:52, David Brown wrote:
> [...]
>>>>    It is not something I have seen on other compilers, and I've used
>>>>> quite a large number over the years for far smaller and slower devices
>>>>> than you are describing here.
>>>>>
>>>>> The way you handle this with gcc has already been covered - you use
>>>>> __builtin_unreachable() to tell the compiler how to optimise for "this
>>>>> can't happen" cases.  clang supports __builtin_unreachable() too, and
>>>>> MSVC has "__assume(false)" that has the same effect.
>>>>>
>>>>
>>>> How would that work in that case? There doesn't appear to be a bit of
>>>> code to hang that onto, unless you specifically create an empty block
>>>> for the purpose, guarded by the same sort of range check you want to 
>>>> avoid.
>>>
>>> The normal situation would be :
>>>
>>>     switch (x) {
>>>         case 1 : handle1(); break;
>>>         case 2 : handle2(); break;
>>>         case 4 : handle4(); break;
>>>         default : __builtin_unreachable();    // gcc
>>>         default : __assume(0);            // msvc
>>>     }
>>
>> As I said, you may want to reserve default: for x==3.
>>
>>>
>>>
>>>
>>> If you want to say x == 3 is a "do nothing" situation, but you want to
>>> tell the compiler it doesn't need to check for x < 1 or x > 4, then you
>>> do so simply and clearly:
>>>
>>>     if ((x < 1) || (x > 4)) __builtin_unreachable();    // gcc
>>>     __assume((x >= 1) && (x <= 4));                // msvc
>>
>> This is really ugly and may involve some compilers (eg. mine) adding 
>> all those extra checks.
>>
> 
> __builtin_unreachable() is a GCC extension (and similarly __assume() for 
> msvc) that is supposed to have no meaning other than for GCC. And that 
> meaning is an instruction for the compiler to consider that range for 
> 'x' impossible to happen, and optimize accordingly - not to add any 
> extra check.
> The fact that your compiler may interpret this instruction differently 
> is irrelevant because, well, this is a GCC-specific extension - in fact 
> your compiler should just /refuse/ to compile the code because 
> __builtin_unreachable() should be undefined in your implementation.
> 
> Ugliness is an intentional feature of implementation-specific extensions.

Actually my attention was on the GCC extension. The MSVC version is not 
too bad, and would be similar to what I'd come up to give an hint about 
the values of a variable. (I'd use 'assume x in 1..4')

However both rather leave open what should be done with that 
information. You have to hope a compiler will put it to some use in a 
subsequence switch(x) statement, but what happens if intervening code 
makes x less well-defined, such as ++x?

Suppose the switch is switch (x-2)?

This is why a way of directly hinting at the behaviour of switch is 
preferable IMO. Then you /know/ what's going to happen.

There are other ways of doing this: instead of one 'default:' branch, 
there can be two, one executed for gaps in the range (of minimum to 
maximum case values), the other for values outside the range. This is 
where it would make more sense for an unreachable() attribute to go.

(I'm not going to propose C syntax for this. In my stuff, having two 
kinds of 'else', as it is there, is an intriguing idea. It can at least 
be used to trap out of range indices in a working program.)

[toc] | [prev] | [next] | [standalone]


#161793

FromManfred <noname@add.invalid>
Date2021-07-09 18:41 +0200
Message-ID<sc9u7b$i1i$1@gioia.aioe.org>
In reply to#161789
On 7/9/2021 5:07 PM, Bart wrote:
> On 09/07/2021 14:35, Manfred wrote:
>> On 7/9/2021 12:09 PM, Bart wrote:
>>> On 09/07/2021 09:01, David Brown wrote:
>>>> On 08/07/2021 18:51, Bart wrote:
>>>>> On 08/07/2021 16:52, David Brown wrote:
>> [...]
>>>>>    It is not something I have seen on other compilers, and I've used
>>>>>> quite a large number over the years for far smaller and slower 
>>>>>> devices
>>>>>> than you are describing here.
>>>>>>
>>>>>> The way you handle this with gcc has already been covered - you use
>>>>>> __builtin_unreachable() to tell the compiler how to optimise for 
>>>>>> "this
>>>>>> can't happen" cases.  clang supports __builtin_unreachable() too, and
>>>>>> MSVC has "__assume(false)" that has the same effect.
>>>>>>
>>>>>
>>>>> How would that work in that case? There doesn't appear to be a bit of
>>>>> code to hang that onto, unless you specifically create an empty block
>>>>> for the purpose, guarded by the same sort of range check you want 
>>>>> to avoid.
>>>>
>>>> The normal situation would be :
>>>>
>>>>     switch (x) {
>>>>         case 1 : handle1(); break;
>>>>         case 2 : handle2(); break;
>>>>         case 4 : handle4(); break;
>>>>         default : __builtin_unreachable();    // gcc
>>>>         default : __assume(0);            // msvc
>>>>     }
>>>
>>> As I said, you may want to reserve default: for x==3.
>>>
>>>>
>>>>
>>>>
>>>> If you want to say x == 3 is a "do nothing" situation, but you want to
>>>> tell the compiler it doesn't need to check for x < 1 or x > 4, then you
>>>> do so simply and clearly:
>>>>
>>>>     if ((x < 1) || (x > 4)) __builtin_unreachable();    // gcc
>>>>     __assume((x >= 1) && (x <= 4));                // msvc
>>>
>>> This is really ugly and may involve some compilers (eg. mine) adding 
>>> all those extra checks.
>>>
>>
>> __builtin_unreachable() is a GCC extension (and similarly __assume() 
>> for msvc) that is supposed to have no meaning other than for GCC. And 
>> that meaning is an instruction for the compiler to consider that range 
>> for 'x' impossible to happen, and optimize accordingly - not to add 
>> any extra check.
>> The fact that your compiler may interpret this instruction differently 
>> is irrelevant because, well, this is a GCC-specific extension - in 
>> fact your compiler should just /refuse/ to compile the code because 
>> __builtin_unreachable() should be undefined in your implementation.
>>
>> Ugliness is an intentional feature of implementation-specific extensions.
> 
> Actually my attention was on the GCC extension. The MSVC version is not 
> too bad, and would be similar to what I'd come up to give an hint about 
> the values of a variable. (I'd use 'assume x in 1..4')

Good point.

> 
> However both rather leave open what should be done with that 
> information. You have to hope a compiler will put it to some use in a 
> subsequence switch(x) statement, but what happens if intervening code 
> makes x less well-defined, such as ++x?

I guess the fact is that this kind of extensions are tightly coupled to 
the internal mechanics of the compiler, and from this perspective 
__builtin_unreachable()  may be gcc's way of handling this scenario (I 
trust David on this, I am just saying this apparent "oddity" is simply 
explained by gcc's internals, which is something they don't have to 
justify to anyone outside their own project)

> 
> Suppose the switch is switch (x-2)?
> 
> This is why a way of directly hinting at the behaviour of switch is 
> preferable IMO. Then you /know/ what's going to happen.
> 
> There are other ways of doing this: instead of one 'default:' branch, 
> there can be two, one executed for gaps in the range (of minimum to 
> maximum case values), the other for values outside the range. This is 
> where it would make more sense for an unreachable() attribute to go.

Yes, but this would be impact the language at the syntax level. 
Extensions should be kept orthogonal to the core language syntax rules 
when possible, I think.

> 
> (I'm not going to propose C syntax for this. In my stuff, having two 
> kinds of 'else', as it is there, is an intriguing idea. It can at least 
> be used to trap out of range indices in a working program.)
> 
> 

[toc] | [prev] | [next] | [standalone]


#161813

FromDavid Brown <david.brown@hesbynett.no>
Date2021-07-10 11:02 +0200
Message-ID<scbnmr$rej$1@dont-email.me>
In reply to#161793
On 09/07/2021 18:41, Manfred wrote:
> On 7/9/2021 5:07 PM, Bart wrote:
>> On 09/07/2021 14:35, Manfred wrote:
>>> On 7/9/2021 12:09 PM, Bart wrote:
>>>> On 09/07/2021 09:01, David Brown wrote:
>>>>> On 08/07/2021 18:51, Bart wrote:
>>>>>> On 08/07/2021 16:52, David Brown wrote:
>>> [...]
>>>>>>    It is not something I have seen on other compilers, and I've used
>>>>>>> quite a large number over the years for far smaller and slower
>>>>>>> devices
>>>>>>> than you are describing here.
>>>>>>>
>>>>>>> The way you handle this with gcc has already been covered - you use
>>>>>>> __builtin_unreachable() to tell the compiler how to optimise for
>>>>>>> "this
>>>>>>> can't happen" cases.  clang supports __builtin_unreachable() too,
>>>>>>> and
>>>>>>> MSVC has "__assume(false)" that has the same effect.
>>>>>>>
>>>>>>
>>>>>> How would that work in that case? There doesn't appear to be a bit of
>>>>>> code to hang that onto, unless you specifically create an empty block
>>>>>> for the purpose, guarded by the same sort of range check you want
>>>>>> to avoid.
>>>>>
>>>>> The normal situation would be :
>>>>>
>>>>>     switch (x) {
>>>>>         case 1 : handle1(); break;
>>>>>         case 2 : handle2(); break;
>>>>>         case 4 : handle4(); break;
>>>>>         default : __builtin_unreachable();    // gcc
>>>>>         default : __assume(0);            // msvc
>>>>>     }
>>>>
>>>> As I said, you may want to reserve default: for x==3.
>>>>
>>>>>
>>>>>
>>>>>
>>>>> If you want to say x == 3 is a "do nothing" situation, but you want to
>>>>> tell the compiler it doesn't need to check for x < 1 or x > 4, then
>>>>> you
>>>>> do so simply and clearly:
>>>>>
>>>>>     if ((x < 1) || (x > 4)) __builtin_unreachable();    // gcc
>>>>>     __assume((x >= 1) && (x <= 4));                // msvc
>>>>
>>>> This is really ugly and may involve some compilers (eg. mine) adding
>>>> all those extra checks.
>>>>
>>>
>>> __builtin_unreachable() is a GCC extension (and similarly __assume()
>>> for msvc) that is supposed to have no meaning other than for GCC. And
>>> that meaning is an instruction for the compiler to consider that
>>> range for 'x' impossible to happen, and optimize accordingly - not to
>>> add any extra check.
>>> The fact that your compiler may interpret this instruction
>>> differently is irrelevant because, well, this is a GCC-specific
>>> extension - in fact your compiler should just /refuse/ to compile the
>>> code because __builtin_unreachable() should be undefined in your
>>> implementation.
>>>
>>> Ugliness is an intentional feature of implementation-specific
>>> extensions.
>>
>> Actually my attention was on the GCC extension. The MSVC version is
>> not too bad, and would be similar to what I'd come up to give an hint
>> about the values of a variable. (I'd use 'assume x in 1..4')
> 
> Good point.
> 
>>
>> However both rather leave open what should be done with that
>> information. You have to hope a compiler will put it to some use in a
>> subsequence switch(x) statement, but what happens if intervening code
>> makes x less well-defined, such as ++x?
> 
> I guess the fact is that this kind of extensions are tightly coupled to
> the internal mechanics of the compiler, and from this perspective
> __builtin_unreachable()  may be gcc's way of handling this scenario (I
> trust David on this, I am just saying this apparent "oddity" is simply
> explained by gcc's internals, which is something they don't have to
> justify to anyone outside their own project)
> 

In gcc's internal tree representation of code, they have a node type for
"undefined behaviour", which is used in some passes to establish facts
about expressions and values, such as possible ranges for data, and it
is used to eliminate branches that lead nowhere (i.e., the compiler
assumes undefined behaviour can't happen, or that the programmer doesn't
care what happens if it does).  __builtin_unreachable() translates
directly to this "undefined behaviour" node.

>>
>> Suppose the switch is switch (x-2)?
>>
>> This is why a way of directly hinting at the behaviour of switch is
>> preferable IMO. Then you /know/ what's going to happen.
>>
>> There are other ways of doing this: instead of one 'default:' branch,
>> there can be two, one executed for gaps in the range (of minimum to
>> maximum case values), the other for values outside the range. This is
>> where it would make more sense for an unreachable() attribute to go.
> 
> Yes, but this would be impact the language at the syntax level.
> Extensions should be kept orthogonal to the core language syntax rules
> when possible, I think.

Agreed.

> 
>>
>> (I'm not going to propose C syntax for this. In my stuff, having two
>> kinds of 'else', as it is there, is an intriguing idea. It can at
>> least be used to trap out of range indices in a working program.)
>>
>>
> 

[toc] | [prev] | [next] | [standalone]


#161795

FromDavid Brown <david.brown@hesbynett.no>
Date2021-07-09 19:20 +0200
Message-ID<sca0gf$hud$1@dont-email.me>
In reply to#161789
On 09/07/2021 17:07, Bart wrote:
> On 09/07/2021 14:35, Manfred wrote:
>> On 7/9/2021 12:09 PM, Bart wrote:
>>> On 09/07/2021 09:01, David Brown wrote:
>>>> On 08/07/2021 18:51, Bart wrote:
>>>>> On 08/07/2021 16:52, David Brown wrote:
>> [...]
>>>>>    It is not something I have seen on other compilers, and I've used
>>>>>> quite a large number over the years for far smaller and slower
>>>>>> devices
>>>>>> than you are describing here.
>>>>>>
>>>>>> The way you handle this with gcc has already been covered - you use
>>>>>> __builtin_unreachable() to tell the compiler how to optimise for
>>>>>> "this
>>>>>> can't happen" cases.  clang supports __builtin_unreachable() too, and
>>>>>> MSVC has "__assume(false)" that has the same effect.
>>>>>>
>>>>>
>>>>> How would that work in that case? There doesn't appear to be a bit of
>>>>> code to hang that onto, unless you specifically create an empty block
>>>>> for the purpose, guarded by the same sort of range check you want
>>>>> to avoid.
>>>>
>>>> The normal situation would be :
>>>>
>>>>     switch (x) {
>>>>         case 1 : handle1(); break;
>>>>         case 2 : handle2(); break;
>>>>         case 4 : handle4(); break;
>>>>         default : __builtin_unreachable();    // gcc
>>>>         default : __assume(0);            // msvc
>>>>     }
>>>
>>> As I said, you may want to reserve default: for x==3.
>>>
>>>>
>>>>
>>>>
>>>> If you want to say x == 3 is a "do nothing" situation, but you want to
>>>> tell the compiler it doesn't need to check for x < 1 or x > 4, then you
>>>> do so simply and clearly:
>>>>
>>>>     if ((x < 1) || (x > 4)) __builtin_unreachable();    // gcc
>>>>     __assume((x >= 1) && (x <= 4));                // msvc
>>>
>>> This is really ugly and may involve some compilers (eg. mine) adding
>>> all those extra checks.
>>>
>>
>> __builtin_unreachable() is a GCC extension (and similarly __assume()
>> for msvc) that is supposed to have no meaning other than for GCC. And
>> that meaning is an instruction for the compiler to consider that range
>> for 'x' impossible to happen, and optimize accordingly - not to add
>> any extra check.
>> The fact that your compiler may interpret this instruction differently
>> is irrelevant because, well, this is a GCC-specific extension - in
>> fact your compiler should just /refuse/ to compile the code because
>> __builtin_unreachable() should be undefined in your implementation.
>>
>> Ugliness is an intentional feature of implementation-specific extensions.

That's not true, IME.  But avoiding any conceivable clash with otherwise
valid code is an intentional feature, hence double underscores are common.

> 
> Actually my attention was on the GCC extension. The MSVC version is not
> too bad, and would be similar to what I'd come up to give an hint about
> the values of a variable. (I'd use 'assume x in 1..4')
> 

Personally, I use a macro :


void __attribute__((error("Assume failed"))) assumeFailed(void);

// The compiler can assume that "x" is true, and optimise or warn
// accordingly
// If the compiler can see that the assume will fail, it gives an error
#define assume(x) \
                do { \
                        if (__builtin_constant_p(!(x))) { \
                                if (!(x)) { \
                                        assumeFailed(); \
                                } \
                        } \
                        if (!(x)) __builtin_unreachable(); \
                } while (0)


But in the context of a discussion like this, it usually makes sense to
write the feature out in its "raw" form, even if some people think it is
ugly.

> However both rather leave open what should be done with that
> information.

You are giving the compiler and any programmer reading the code a bit of
extra information.  How much the compiler can use it to optimise code is
up to the quality of the compiler, just like any other code.

> You have to hope a compiler will put it to some use in a
> subsequence switch(x) statement, but what happens if intervening code
> makes x less well-defined, such as ++x?

The compiler knows the assumptions you give it hold when they are given.
 If you increment x, it knows that now x >= 2 and x <= 5.  (Again, what
it can do with this information is a matter of quality of optimisation.)

> 
> Suppose the switch is switch (x-2)?
> 
> This is why a way of directly hinting at the behaviour of switch is
> preferable IMO. Then you /know/ what's going to happen.

No, you don't /know/ what is going to happen.  You would merely be
giving a hint, and it is up to the compiler to generate code that has
the same effect - as efficiently or inefficiently as it wants.  You
don't /know/ you'll get a jump table, or any other code generated from
the switch.  Why would you think you know about what might be generated
by a hint about "naked" switches?  The compiler guarantees observable
behaviour, not generated code.

> 
> There are other ways of doing this: instead of one 'default:' branch,
> there can be two, one executed for gaps in the range (of minimum to
> maximum case values), the other for values outside the range. This is
> where it would make more sense for an unreachable() attribute to go.
> 
> (I'm not going to propose C syntax for this. In my stuff, having two
> kinds of 'else', as it is there, is an intriguing idea. It can at least
> be used to trap out of range indices in a working program.)
> 

It is a silly idea, IMHO.  Either the value is valid or it is not.  I
see no benefits in trying to say that some values are more invalid than
others just because they are outside a certain range.

[toc] | [prev] | [next] | [standalone]


Page 1 of 3  [1] 2 3  Next page →

Back to top | Article view | comp.lang.c


csiph-web