On Wed, Nov 06, 2024 at 02:44:12PM +0300, Alexander Monakov wrote: > I didn't see a discussion of a more gentle approach where instead of > replacing the result of malloc with a non-zero constant, we would change > > tmp = malloc(sz); > > to > > tmp = (void *)(sz <= SIZE_MAX / 2); > > and let the following optimizations fold that further based on sz's range. > Is there some issue with that that I'm not seeing?
Yes. Not all malloc implementations will return NULL for sz > SIZE_MAX / 2. And users could rely on malloc failing in other cases. Like tmp1 = malloc(SIZE_MAX / 2); tmp2 = malloc(SIZE_MAX / 2); tmp3 = malloc(SIZE_MAX / 2); assert (!tmp1 || !tmp2 || !tmp3); free (tmp3); free (tmp2); free (tmp1); So, either we try to cope with all cases where user could rely on malloc failing (for the unconditional malloc/free removal case perhaps looking at errno, for the conditional one testing the conditions and remembering it) and essentially never optimize anything, or document we do this transformation and tell users to use some flag if they test for side-effects which are not guaranteed with the optimization. Or make it a non-default flag and convince WG14 to add some wording that would allow the optimization, like WG21 added https://eel.is/c++draft/expr.new#14 https://eel.is/c++draft/expr.delete#6 That is, allow the malloc etc./free pair to be omitted or extended. int *p = new int; delete p; can be validly omitted, or int *p = new int; foo (p); delete p; replaced with int temp; int *p = &temp; foo (p); or int *p = new int[32]; int *q = new int[64]; ... delete q; delete p; extended into int *p = new int[96]; int *q = p + 32; ... delete p; (I guess this one only assuming it can prove it sees all the deallocations of both). Though, unsure how that https://eel.is/c++draft/expr.new#14 interacts with https://eel.is/c++draft/expr.new#8 and we'd have to check if we do the size checking before the ::operator new/new[] calls or it ::operator new just throws/returns NULL. Jakub