[Swan-dev] const version of str_*()

Andrew Cagney andrew.cagney at gmail.com
Mon Sep 16 17:02:53 UTC 2019

On Mon, 16 Sep 2019 at 02:22, D. Hugh Redelmeier <hugh at mimosa.com> wrote:

> | From: Andrew Cagney <andrew.cagney at gmail.com>
> Nice analysis.
> | But here's a different problem.  We've code micro-optimized as follows:
> |
> |   char buf[ID_LEN]
> |   idtoa(&id1, buf);
> |
> |   if (dbg) printf("id1 = %s\n", buf);
> |   ...
> |   if (dbg) printf("id 2 = %s\n", buf);
> |
> | presumably to minimise the number of string conversions.  Except, with
> | certain irony, when !dbg and things matter, it will make an
> | unnecessary call.
> Generally those are cases where I was not bold enough to eliminate
> that code when I converted to the new clearer routines.
> I support your being bold.  (Except when I disagree :-)
> Sometimes these things have been moved outside loops.  But even in
> these cases your new routine would be helpful without moving the call
> into the loop.

... if it isn't a debug-log

> | However, I wonder if we we can tweak this further and eliminate the
> | need to declare the buffers; and the desire to optimise, vis:
> |
> |    id_buf id_str(const id_t *id) __attribute__((const))
> |    ...
> |    printf("id1 = %s\n", id_str(&id1).buf);
> |    printf("id2 = %s\n", id_str(&id2).buf);
> |    printf("id1 = %s\n", id_str(&id1).buf);
> I like this idea, but only if you drop your objection to inline
> functions.
A function that dereferences a pointer arg cannot have
> __attribute__((const)).  So you must adjust the id_str function to
> receive the id_t "by-value".
Ah, this clause:

Note that a function that has pointer arguments and examines the data
pointed to must *not* be declared const if the pointed-to data might change
between successive invocations of the function. In general, since a
function cannot distinguish data that might change from data that cannot,
const functions should never take pointer or, in C++, reference arguments.
Likewise, a function that calls a non-const function usually must not be
const itself.

good catch, but it honestly leaves me confused.  I'm guessing it's a
limitation of the compiler - it doesn't track if the values contents
changed between invocations

Fortunately we've plan B: "pure".  It does seem to be a better fit:

Some common examples of pure functions are strlen or memcmp.

> id_buf id_str(const id_t id) __attribute__((const))
> (With inlining there should be no cost to this change.)
Using pure removes the need for this.

BTW, I'm not a particular fan of how all the functions take const type
pointers rather than values - I've been holding my nose.


- the compiler already in-lines jam_<type>() into str_<type>() (I know this
from debugging);  forcing inline <type>_str() would just bloat the calling
functions (and the added code would be "cold" as it is typically if(debug))

- modern ABIs, as in anything from the '90's on, when given "struct s v",
stuff small struts into registers and pass large ones by reference - the
'80's dogma of 'const struct *' being faster is dead

OTOH, for both of these we'll never see any measurable benefit - Pluto
spends its time computing DH and/or performing increasingly obscure
O(#STATE) operations.

But there's another problem.
> I don't think that C guarantees that the id_buf value would live long
> enough.  The expression is more explicitly written
>         &id_str(&id1).buf[0]
I wondered, but figured C couldn't be so dumb.
Clearly I'm wrong.

As soon as the & is applied, I believe that C's semantics allows the
> temporary id_buf value to be discarded by the implementation.  Before
> the pointer is passed to printf.  So the pointer must not be
> dereferenced.  Thus this code probably ventures into the dreaded
> "undefined behaviour".
> In my brief participation in the C++ Standards process, I learned that
> this was called the "lifetime of temporaries problem".  It's a real
> mess in C++ but I think that it has been nailed down (with an
> arbitrary hack).  There's much less need in C so I don't think that it
> has been addressed.
> Summary C++ has a hack that probably makes your function correct but C
> does not.  Or more correctly, the idiomatic use of the function --
> the function itself is well-defined.
> See, for example
> <
> https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.cbclx01/lifetime_temp.htm
> >
> A less ambitious use of your function might still be an improvement
> over the current code.  Perhaps not worth the effort to refactor.
>         const id_buf b1 = id_str(id1);
>         printf("id1 = %s\n", b1.buf);
>         const id_buf b2 = id_str(id2);
>         printf("id2 = %s\n", b2.buf);
>         const id_buf b3 = id_str(id3);
>         printf("id3 = %s\n", b3.buf);
Perhaps.  It runs the risk of someone applying the "obvious" cleanup:
     printf("id3 = %s\n", id_str(id3));

Another bug I forgot to mention is:

   const char *s = "foo";
   if (val != NULL) {
      type_buf buf; /* XXX: must be at same scope as S */
      s = str_type(&valu, &buf);

| https://lwn.net/Articles/285332/
> |
> | - the need to declare the buffer is gone
> | - the compiler can eliminate duplicate calls
> | - the odds of passing wrong buffers is reduced further
> Yes.
> I thought that C was going to adopt something like that but I don't
> immediately see anything in the C-11 standard.
> | it should also be efficient.  Small structures are returned in 1-2
> | registers; and larger structures are returned by reference, the above
> | is compiled as:
> |
> |    id_buf tmp;
> |    str_id(&tmp, &id)
> |    tmp.buf
> |
> | making it equivalent to the old str_id() call.
> This operational model is not the definition of C.  I'm against
> exploiting something that is technically Undefined Behaviour.
> _______________________________________________
> Swan-dev mailing list
> Swan-dev at lists.libreswan.org
> https://lists.libreswan.org/mailman/listinfo/swan-dev
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.libreswan.org/pipermail/swan-dev/attachments/20190916/290e8c55/attachment.html>

More information about the Swan-dev mailing list