« first day (1 day earlier)    last day (16 days later) » 

usr
10:58 AM
yeah I guess it makes sense to allow the programmer to pick from a defined set of error behaviors
seems to me that the best way to do that syntactically is through library functions, such as add(x, y, errorbehavior.requirevalue | requiredeterministicresult). This is a very rarely used facility after all. In almost all places where you add two numbers you want to be statically sure that they do not overflow. This goes for any language (such as C#). Not having overflow is, in my mind, like any other precondition that needs to be satisfied when invoking an operation.
That said a mode that makes certain C UB things less dangerous seems like a good idea and a wise choice for 99% of the code.
@supercat
 
 
4 hours later…
3:09 PM
@usr: Most programs have one of four requirements with respect to inputs which would prevent an algorithm from being carried out as specified; (1) Program must strictly-define an alternate algorithm in all cases where the primary algorithm cannot be carried out; (2) program must not produce seemingly-valid output unless the algorithm was carried out successfully; (3) Program may substitute a any of a broadly-defined range of algorithms, some of which may produce seemingly-valid output, ...
...if the primary algorithm cannot be carried out; (4) Program may assume that the supplier of the data is willing to stake the fate of the world on its validity, and may do whatever it likes in retaliation if the supplier failed to do so.
Prior to C, a lot of programming languages were optimized for requirement #2. Most C implementations have historically been optimized for requirement #3, though floating-point NaN works very nicely for #1. In many real-world applications which must deal with untrusted data, some data must be treated as #1, some as #2, and a lot as #3, but none as #4 unless the nature of the algorithm would allow it to handle any incoming bit pattern.
[I've sometimes listed the requirements differently, but I've since decided that it's worth splitting out a requirement that a program not generate seemingly-valid output; in cases where an algorithm is supposed to populate an array and return a success/failure indication, being able to leave the array holding arbitrary contents in case of failure may enable some optimizations which would not be possible if the array contents had to be strictly defined in all cases].
Semantically, I think it is helpful to think in terms of some variables holding sanitized content and others holding unsanitized content. Variables whose content is considered sanitized should never be used in any fashion which would cause unexpected overflows or other such problems, but it should not be necessary to sanitize everything a program processes if nothing that is done with unsanitized variables can affect sanitized variables. Suppose, for example, that one has the task of...
...writing a method which reads N pairs of int16_t variables [e.g. representing the instantaneous left-side and right-side amplitudes of a waveform, all of which will--if valid--be in the range +/-16383] and needs to return the maximum left-right difference in case all inputs are valid, and any number if any inputs are invalid.
Suppose further that the code should be able to run optimally either on a 16-bit DSP or an ARM. How should one write it?
If one could guarantee that inputs will always be valid:
` int max_diff(int16_t *dat, int16_t n)`
` {`
` int max = -32768;`
` for (; n!=0; n--)`
` {`
` int diff = dat[1]-dat[0];`
` if (diff > max) max = diff;`
` dat+=2;`
` }`
` }`
[add a `return max`, obviously].
On the DSP, performing the subtraction so as to wrap mod 65536 rather than overflowing would be slower than letting it overflow. The ARM would also impose a penalty for constraining the result mod 65536. Optimal code for one DSP I've used would probably be obtained by using a `long` for `diff` but an `int` for `max, but that would probably confuse anyone reading the code; the other DSP would probably yield the best code if both places used `long`...
...but that would impose a significant speed penalty on the first DSP (it has a single 32-bit accumulator, but any particular instruction can only load or store 16 bits of it at a time); the other has two 32-bit accumulators, and while I haven't tested this code with it, I'd expect the compiler could probably keep diff and max in those accumulators.
@usr: Any idea how the above code could be written in a readable fashion that would allow standard-compliant compilers for the 8088, TMS C2xx DSP, TMS 32050 DSP, x86, and ARM7-TDMI to produce the optimal code meeting the stated requirements? The pre-2009 compilers I know of for the above would all generate code meeting requirements, and I know of no way of writing the above without using UB that wouldn't generate worse code for at least some of them.
@usr: BTW, to yield optimal code on all the platforms, diff must be type int; making it int16_t would greatly degrade code generation on the ARM. IMHO, there should be a type which would be format-compatible and aliasing-compatible with int16_t, but where storing a value outside the range -32768..32767 would allow subsequent reads to behave as any number (not necessarily within the range of int) that is congruent to the value stored mod 65536. (such type would be usable here)
 
 
5 hours later…
usr
8:44 PM
I have no good ideas but it seems that compiler could be taught to widen or narrow integers if that is convenient for codegen. Not an obvious optimization, though.
 

« first day (1 day earlier)    last day (16 days later) »