Rocksolid Light

Welcome to novaBBS (click a section below)

mail  files  register  newsreader  groups  login

Message-ID:  

It's hard to tune heavily tuned code. :-) -- Larry Wall in <199801141725.JAA07555@wall.org>


devel / comp.lang.c / Re: naked switches

Re: naked switches

<sc9b35$g6s$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=17366&group=comp.lang.c#17366

  copy link   Newsgroups: comp.lang.c
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: david.br...@hesbynett.no (David Brown)
Newsgroups: comp.lang.c
Subject: Re: naked switches
Date: Fri, 9 Jul 2021 13:14:45 +0200
Organization: A noiseless patient Spider
Lines: 189
Message-ID: <sc9b35$g6s$1@dont-email.me>
References: <06a0049a-2d7d-40f0-899a-fb35c9a15d5fn@googlegroups.com>
<8735sp8cbm.fsf@nosuchdomain.example.com>
<255c09e7-2365-4983-ad4d-3bfc83cf04e7n@googlegroups.com>
<31342574-cb69-4ad6-8576-387dcf9caf70n@googlegroups.com>
<sc76v8$8ah$1@dont-email.me> <sc7aeg$10u$1@dont-email.me>
<sc8von$8fo$1@dont-email.me> <sc979l$n65$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Injection-Date: Fri, 9 Jul 2021 11:14:46 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="7ff94317d495bb646f4ebc938aee1ba8";
logging-data="16604"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+cVYBc6W2DCqIG9M/PejOz1YqEoOdS9V8="
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101
Thunderbird/68.10.0
Cancel-Lock: sha1:V6e1F2MTiMZTyg+/O+3DaVOwmg0=
In-Reply-To: <sc979l$n65$1@dont-email.me>
Content-Language: en-GB
 by: David Brown - Fri, 9 Jul 2021 11:14 UTC

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".

SubjectRepliesAuthor
o naked switches

By: Robert Finch on Wed, 7 Jul 2021

59Robert Finch
server_pubkey.txt

rocksolid light 0.9.8
clearnet tor