Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #161725 > unrolled thread
| Started by | Robert Finch <robfi680@gmail.com> |
|---|---|
| First post | 2021-07-07 16:39 -0700 |
| Last post | 2021-09-30 18:04 +0200 |
| Articles | 20 on this page of 59 — 11 participants |
Back to article view | Back to comp.lang.c
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 →
| From | Robert Finch <robfi680@gmail.com> |
|---|---|
| Date | 2021-07-07 16:39 -0700 |
| Subject | naked 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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2021-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]
| From | Kaz Kylheku <563-365-8930@kylheku.com> |
|---|---|
| Date | 2021-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2021-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]
| From | Keith Thompson <Keith.S.Thompson+u@gmail.com> |
|---|---|
| Date | 2021-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]
| From | Malcolm McLean <malcolm.arthur.mclean@gmail.com> |
|---|---|
| Date | 2021-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]
| From | Robert Finch <robfi680@gmail.com> |
|---|---|
| Date | 2021-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2021-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2021-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2021-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2021-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2021-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2021-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]
| From | Manfred <noname@add.invalid> |
|---|---|
| Date | 2021-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2021-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]
| From | Manfred <noname@add.invalid> |
|---|---|
| Date | 2021-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]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2021-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]
| From | Manfred <noname@add.invalid> |
|---|---|
| Date | 2021-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2021-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2021-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