Path: ...!2.eu.feeder.erje.net!feeder.erje.net!eternal-september.org!feeder3.eternal-september.org!news.eternal-september.org!.POSTED!not-for-mail From: Bart Newsgroups: comp.lang.c Subject: Re: how cast works? Date: Thu, 8 Aug 2024 19:01:56 +0100 Organization: A noiseless patient Spider Lines: 130 Message-ID: References: <20240808193203.00006287@yahoo.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Injection-Date: Thu, 08 Aug 2024 20:01:57 +0200 (CEST) Injection-Info: dont-email.me; posting-host="0ec5f4cd8f82378826952c114447df93"; logging-data="154512"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/j5zc3wj/hxbqpYLZH669j" User-Agent: Mozilla Thunderbird Cancel-Lock: sha1:zbi0XHzIOztLs8Rev9wiw5J/z6w= Content-Language: en-GB In-Reply-To: Bytes: 5688 On 08/08/2024 18:50, Thiago Adams wrote: > On 08/08/2024 14:29, Bart wrote: >> On 08/08/2024 17:32, Michael S wrote: >>  > On Thu, 8 Aug 2024 14:23:44 +0100 >>  > Bart wrote: >>  >> Try godbolt.org. Type in a fragment of code that does different kinds >>  >> of casts (it needs to be well-formed, so inside a function), and see >>  >> what code is produced with different C compilers. >>  >> >>  >> Use -O0 so that the code isn't optimised out of existence, and so >>  >> that you can more easily match it to the C ource. >>  >> >>  >> >>  > >>  > >>  > I'd recommend an opposite - use -O2 so the cast that does nothing >>  > optimized away. >>  > >>  > int foo_i2i(int x) { return (int)x; } >>  > int foo_u2i(unsigned x) { return (int)x; } >>  > int foo_b2i(_Bool x) { return (int)x; } >>  > int foo_d2i(double x) { return (int)x; } >> The OP is curious as to what's involved when a conversion is done. >> Hiding or eliminating code isn't helpful in that case; the results can >> also be misleading: >> >> Take this example: >> >>    void fred(void) { >>     _Bool b; >>       int i; >>       i=b; >>    } >> >> Unoptimised, it generates this code: >> >>          push    rbp >>          mov     rbp, rsp >> >>          mov     al, byte ptr [rbp - 1] >>          and     al, 1 >>          movzx   eax, al >>          mov     dword ptr [rbp - 8], eax >> >>          pop     rbp >>          ret >> >> You can see from this that a Bool occupies one byte; it is masked to >> 0/1 (so it doesn't trust it to contain only 0/1), then it is widened >> to an int size. >> >> With optimisation turned on, even at -O1, it produces this: >> >>          ret >> >> That strikes me as rather less enlightening! >> >> Meanwhile your foo_b2i function contains this optimised code: >> >>          mov     eax, edi >>          ret >> >> The masking and widening is not present. Presumably, it is taking >> advantage of the fact that a _Bool argument will be converted and >> widened to `int` at the callsite even though the parameter type is >> also _Bool. So the conversion has already been done. >> >> You will see this if writing also a call to foo_b2i() and looking at >> the /non-elided/ code. >> >> The unoptimised code for foo_b2i is pretty awful (like masking twice, >> with a pointless write to memory between them). But sometimes with gcc >> there is no sensible middle ground between terrible code, and having >> most of it eliminated. >> >> The unoptimised code from my C compiler for foo_b2i, excluding >> entry/exit code, is: >> >>      movsx   eax, byte [rbp + foo_b2i.x] >> >> My compiler assumes that a _Bool type already contains 0 or 1. >> >> >> > > If you are doing constant expression in your compiler, then you have the > same problem (casts) I am solving in cake. > > For instance > static_assert((unsigned char)1234 == 210); > > is already working in my cake. I had to simulate this cast. > > Previously, I was doing all computations with bigger types for constant > expressions. Then I realize compile time  must work as the runtime. > > For constexpr the compiler does not accept initialization invalid types. > for instance. > >  constexpr char s = 12345; > > :6:21: error: constexpr initializer evaluates to 12345 which is > not exactly representable in type 'const char' >     6 |  constexpr char s = 12345; > > > I am also checking all wraparound and overflow in constant expressions. > I have a warning when the computed value is different from the math value. In my C compiler I have no constexpr (don't know why you got that impression). And I don't check that initialisers for integer types overflow their destination. This is because within the language in general: char c; int i; c = i; Such an assignment is not checked at runtime (and I don't know if this can be warned against, or if a runtime check can be added). This is just how C works: too-large values are silently truncated (there are worse aspects of the language, like being able to do `int i; (&i)[12345];`). But you are presumably superimposing a new stricter language on top. In the case, if my `c = i` assignment was not allowed, how do I get around that; by a cast?