Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.compilers > #2241
| From | Martin Ward <martin@gkc.org.uk> |
|---|---|
| Newsgroups | comp.compilers |
| Subject | Re: Optimization techniques |
| Date | 2019-05-02 10:56 +0100 |
| Organization | Compilers Central |
| Message-ID | <19-05-005@comp.compilers> (permalink) |
| References | <72d208c9-169f-155c-5e73-9ca74f78e390@gkc.org.uk> <910eaf6f-f9ae-9c02-5052-f06474024d96@hesbynett.no> <19-04-027@comp.compilers> |
On 26/04/19 19:46, Martin Ward wrote:
> Note that even knowing that there is undefined behaviour, you still
> may not be able to avoid it by testing for its occurrence: the
> optimiser might spot that you are testing for undefined behaviour and
> optimise away your test, because it is allowed to assume that the
> undefined behaviour can never happen! (This is what actually occurred
> in the last example I gave).
I was a little hasty here. As I now understand it, the real problem
with the example was that undefined behaviour occured before
it was tested for, and this resulted in the test being optimised away:
struct agnx_priv *priv = dev->priv;
if (!dev) return;
... do stuff using dev ...
The assignment *priv = dev->priv is undefined when dev is null
(so could be deleted when dev is null). After the assignment,
the compiler can assume that dev is not null, so can delete
the test (!dev).
Undefined behaviour must be avoided under all circumstances,
even when you do not care what result is computed!
For example, suppose that, in testing for signed overflow,
instead of writing:
signed int sum;
if (((si_b > 0) && (si_a > (INT_MAX - si_b))) ||
((si_b < 0) && (si_a < (INT_MIN - si_b)))) {
/* Handle error */
} else {
sum = si_a + si_b;
}
you were to write:
signed int sum = si_a + si_b;
if (((si_b > 0) && (si_a > (INT_MAX - si_b))) ||
((si_b < 0) && (si_a < (INT_MIN - si_b)))) {
/* Handle error */
}
This is shorter, but possibly less efficient.
However, if overflow is very rare then the efficiency
concern might be considered unimportant.
Since you are not using the value of sum in the case
of overflow, you might think that there is no problem.
But "undefined behaviour" does not simply mean
"the compiler can return any value for sum" but
"the compiler is absolutely unrestricted in the code that
it generates". So the statement:
signed int sum = si_a + si_b;
could compile to code which searches your filesystem for
bank details and uploads them to a server in China
whenever si_a + si_b overflows!
In my opinion, *any* defined behaviour (including nondeterministic
defined behaviour) is preferable to undefined behaviour.
If the compiler were allowed to return any bit pattern as
the result of overflow, then the code above would be OK,
and all the optimisations on signed arithmetic would still
be possible, but your bank details would be safe.
--
Martin
Dr Martin Ward | Email: martin@gkc.org.uk | http://www.gkc.org.uk
G.K.Chesterton site: http://www.gkc.org.uk/gkc | Erdos number: 4
Back to comp.compilers | Previous | Next — Previous in thread | Next in thread | Find similar
Re: Optimization techniques Martin Ward <martin@gkc.org.uk> - 2019-04-26 19:46 +0100
Re: language design and Optimization techniques Kaz Kylheku <847-115-0292@kylheku.com> - 2019-04-26 21:06 +0000
Re: Optimization techniques and consistent results David Brown <david.brown@hesbynett.no> - 2019-04-29 16:31 +0200
Re: Optimization techniques Martin Ward <martin@gkc.org.uk> - 2019-05-02 10:56 +0100
Re: Optimization techniques Kaz Kylheku <847-115-0292@kylheku.com> - 2019-05-02 17:51 +0000
csiph-web