Defeating Optimisation for memcmp()

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Martin

    #1

    Defeating Optimisation for memcmp()

    Please consider the following code fragment. Assume UINT32 is a typedef
    suitable for defining variables of 32 bits, and that ui32 is initialised.

    UINT32 ui32;
    /* ... */
    /* assume ui32 now is holding a value in uc's range */
    unsigned char uc = (unsigned char) ui32; /* tell Lint you know target
    type is smaller */
    cmos->uc = uc;

    /* Check the CMOS write was successful */
    /* NOTE: I know third arg evals to 1, but sizeof is used for readability
    */
    if (memcmp(cmos->uc, &uc, sizeof(unsigned char)) != 0)
    /* CMOS write failed */

    I would expect a decent compiler to optimize away the memcmp(). Would you
    agree that this change will ensure the memcmp is not optimized away?:

    UINT32 ui32;
    /* ... */
    /* assume ui32 now is holding a value in uc's range */
    volatile unsigned char uc = (unsigned char) ui32; /* tell Lint you know
    target type is smaller */
    cmos->uc = uc;


    /* Check the CMOS write was successful */
    if (memcmp(cmos->uc, &uc, sizeof(unsigned char)) != 0)
    /* CMOS write failed */

    Is a call to a library function (memcmp) less likely to be optimised away
    than use of relational operators (a) when one of the operands is volatile,
    and (b) when neither are volatile?

    For example,

    if ( cmos->uc != uc )
    /* CMOS write failed */

    compared to

    if (memcmp(cmos->uc, &uc, sizeof(unsigned char)) != 0)
    /* CMOS write failed */

    Thank-you in advance.

    --
    Martin



  • Martin

    #2
    Re: Defeating Optimisation for memcmp()


    "Ben Bacarisse" wrote:
    Presumably you mean '&cmos->uc'? The same typo appears several times
    so I am not sure.
    It's my mistake Ben - I did miss the ampersand for memcmp()'s first
    argument. It should of course be as you say.

    --
    Martin



    Comment

    • Eric Sosman

      #3
      Re: Defeating Optimisation for memcmp()

      Martin wrote:
      [...]
      Is a call to a library function (memcmp) less likely to be optimised away
      than use of relational operators (a) when one of the operands is volatile,
      and (b) when neither are volatile?
      The call has undefined behavior if *either* operand is
      volatile. 6.7.3p5:

      [...] If an attempt is made to refer to an object
      defined with a volatile-qualified type through use of
      an lvalue with non-volatile-qualified type, the
      behavior is undefined.

      There's a quibble: If memcmp is not implemented in C, it does
      not use C lvalues to access anything at all and thus might
      skirt the prohibition. But then there's 7.1.4p1:

      If an argument to a function has an invalid value (such
      as a value outside the domain of the function, or a
      pointer outside the address space of the program, or a
      null pointer, or a pointer to non-modifiable storage when
      the corresponding parameter is not const-qualified) [...]
      the behavior is undefined.

      True, volatile is not mentioned. However, the list of ways in
      which an argument can be invalid is prefaced by "such as," a
      construct suggestive of a non-exhaustive list.

      Finally, there's 6.3.2.3p2:

      For any qualifier q, a pointer to a non-q-qualified type
      may be converted to a pointer to the q-qualified version
      of the type [...]

      The point is that the conversion in the other direction is not
      described as legal: You can add qualifiers in a conversion, but
      you cannot subtract them. If you hand a pointer-to-volatile to
      memcmp (which expects a pointer-to-const), I think the compiler
      is required to issue a diagnostic -- and if it then accepts and
      runs the program anyhow, all bets are off.

      The solution to your problem is to qualify the "CMOS" thing
      as volatile, and use an ordinary comparison:

      struct {
      ...
      volatile unsigned char uc;
      ...
      } *cmos = ...;

      cmos->uc = uc;
      if (cmos->uc == uc) ...

      The compiler is not permitted to optimize away any of the accesses
      to cmos->uc, because the volatile qualifier declares that those
      accesses have side-effects, just like calls to putchar(). The
      compiler cannot "just know" that the comparison will turn out
      true; it must actually perform it. That's what volatile does.

      --
      Eric Sosman
      esosman@ieee-dot-org.invalid

      Comment

      • Ben Bacarisse

        #4
        Re: Defeating Optimisation for memcmp()

        Eric Sosman <esosman@ieee-dot-org.invalidwrit es:
        Martin wrote:
        >[...]
        >Is a call to a library function (memcmp) less likely to be optimised
        >away than use of relational operators (a) when one of the operands
        >is volatile, and (b) when neither are volatile?
        >
        The call has undefined behavior if *either* operand is
        volatile. 6.7.3p5:
        <snip>
        The point is that the conversion in the other direction is not
        described as legal: You can add qualifiers in a conversion, but
        you cannot subtract them.
        I stand (implicitly) corrected. I should have seen this. This is why
        c.l.c is so worth reading To the OP: ignore (most of) what I wrote!

        --
        Ben.

        Comment

        • Martin

          #5
          Re: Defeating Optimisation for memcmp()


          "Eric Sosman" wrote:
          The solution to your problem is to qualify the "CMOS" thing
          as volatile, and use an ordinary comparison:
          >
          struct {
          ...
          volatile unsigned char uc;
          ...
          } *cmos = ...;
          >
          cmos->uc = uc;
          if (cmos->uc == uc) ...
          >
          The compiler is not permitted to optimize away any of the accesses
          to cmos->uc, because the volatile qualifier declares that those
          accesses have side-effects, just like calls to putchar(). The
          compiler cannot "just know" that the comparison will turn out
          true; it must actually perform it. That's what volatile does.

          Thanks for your comments. I don't think making the cmos variable volatile is
          an option.

          Why can't I make uc volatile?

          volatile unsigned char uc = (unsigned char) ui32;
          /* ... init uc ... */
          cmos->uc = uc;
          if ( cmos->uc != uc )
          /* error writing to CMOS */

          --
          Martin



          Comment

          • Ben Bacarisse

            #6
            Re: Defeating Optimisation for memcmp()

            "Martin" <martin.o_brien @[no-spam]which.netwrites :
            "Eric Sosman" wrote:
            > The solution to your problem is to qualify the "CMOS" thing
            >as volatile, and use an ordinary comparison:
            >>
            >struct {
            > ...
            > volatile unsigned char uc;
            > ...
            >} *cmos = ...;
            >>
            >cmos->uc = uc;
            >if (cmos->uc == uc) ...
            >>
            >The compiler is not permitted to optimize away any of the accesses
            >to cmos->uc, because the volatile qualifier declares that those
            >accesses have side-effects, just like calls to putchar(). The
            >compiler cannot "just know" that the comparison will turn out
            >true; it must actually perform it. That's what volatile does.
            >
            >
            Thanks for your comments. I don't think making the cmos variable volatile is
            an option.
            >
            Why can't I make uc volatile?
            >
            volatile unsigned char uc = (unsigned char) ui32;
            /* ... init uc ... */
            cmos->uc = uc;
            if ( cmos->uc != uc )
            /* error writing to CMOS */
            Eric Sosman's comment was about using memcmp -- specifically that
            passing memcmp a pointer to a volatile object is not permitted. You
            can use a != test but...

            Making uc volatile won't work. The compiler may assume that cmos->uc
            is set as per the assignment (it need not access the object again). I
            can't see a way round this other than making the object that is
            actually volatile, volatile. Forcing the compiler to re-access uc to
            compare it against the value that it may have squirreled away as the
            assumed contents of cmos->uc will not help you.

            --
            Ben.

            Comment

            • CBFalconer

              #7
              Re: Defeating Optimisation for memcmp()

              Eric Sosman wrote:
              >
              .... snip ...
              >
              The compiler is not permitted to optimize away any of the accesses
              to cmos->uc, because the volatile qualifier declares that those
              accesses have side-effects, just like calls to putchar(). The
              compiler cannot "just know" that the comparison will turn out
              true; it must actually perform it. That's what volatile does.
              Not side-effects. The variable may 'spontaneously' change between
              reads. There is no reason to insist on a write between reads.

              --
              Chuck F (cbfalconer at maineline dot net)
              <http://cbfalconer.home .att.net>
              Try the download section.



              --
              Posted via a free Usenet account from http://www.teranews.com

              Comment

              • Chris Torek

                #8
                Re: Defeating Optimisation for memcmp()

                >"Eric Sosman" wrote:
                >> The solution to your problem is to qualify the "CMOS" thing
                >>as volatile, and use an ordinary comparison:
                >>>
                >>struct {
                >> ...
                >> volatile unsigned char uc;
                >> ...
                >>} *cmos = ...;
                (Or, as I tend to prefer, make the structure type's elements
                ordinary, non-"volatile"-qualified, but use a volatile qualifier
                on the pointer itself:

                struct whatever { ... unsigned char uc; ... };
                volatile struct whatever *cmos = ...;

                This allows one to copy the entire data structure into ordinary
                RAM, then manipulate it there without defeating optimization.)
                >>cmos->uc = uc;
                >>if (cmos->uc == uc) ...
                >>>
                >>The compiler is not permitted to optimize away any of the accesses
                >>to cmos->uc, because the volatile qualifier declares that those
                >>accesses have side-effects ...
                (Or rather, that they "may" have side effects, and the compiler
                should assume the worst.)
                >"Martin" <martin.o_brien @[no-spam]which.netwrites :
                >Thanks for your comments. I don't think making the cmos variable
                >volatile is an option.
                Why not? (Neither Eric Sosman's method nor mine generally requires
                much in the way of code changes.)
                >Why can't I make uc volatile?
                You can; it just is silly, and may well not work. (In fact, it is
                only likely to work if the code happens to work with no "volatile"
                qualifiers anyway. That is, adding the volatile qualifier in the
                wrong place is extremely unlikely to help.)

                [I am going to make a name change below, so that "uc" unambiguously
                refers to cmos->uc, and use "temp_v" for the local variable.]
                > volatile unsigned char temp_v = (unsigned char) ui32;
                > cmos->uc = temp_v;
                > if ( cmos->uc != temp_v )
                > /* error writing to CMOS */
                In article <874pft1sjr.fsf @bsb.me.uk>
                Ben Bacarisse <ben.usenet@bsb .me.ukwrote:
                >Making temp_v volatile won't work.
                Well, it *might* work, all depending on details about the
                compiler's internal workings.
                >The compiler may assume that cmos->uc is set as per the assignment
                >(it need not access the object again).
                Right -- for instance, it might generate code of the form:

                ldw cmos_, a3 # so that register a3 = cmos
                ldb -12(sp), d1 # so that register d1 = temp_v
                stb d1, 48(a3) /* cmos->uc = temp_v; */

                ldb -12(sp), d2 # so that register d2 = temp_v
                cmp d1, d2 /* see if cmos->uc == temp_v */
                ...

                Note that temp_v was loaded twice, in case it changed; but the
                compiler could see that 48(a3), which refers to cmos->uc, was set
                from register d1, and -- since it is "ordinary RAM" (even though
                it is not!) it must not have changed, so there was no need to load
                *that* again.
                >I can't see a way round this other than making the object that is
                >actually volatile, volatile.
                Indeed.
                --
                In-Real-Life: Chris Torek, Wind River Systems
                Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
                email: forget about it http://web.torek.net/torek/index.html
                Reading email is like searching for food in the garbage, thanks to spammers.

                Comment

                • Martin

                  #9
                  Re: Defeating Optimisation for memcmp()

                  My apologies - I had meant to submit a thank-you message to all your
                  responses before now.

                  So, thanks for the helpful advice.

                  I have two related questions. According to K&R2:

                  "Except that it should diagnose explicit attempts to change 'const'
                  objects, a compiler may ignore these qualifiers."

                  "These qualifiers" are 'const' and 'volatile'. It seems that this could be
                  an issue. I can carefully implement Eric Sosman's suggestion regarding using
                  a volatile pointer to the structure, but it seems an ANSI/ISO conformant
                  compiler is free to ignore it, and the compiler then could optimise away the
                  following memcmp(). Could someone clarify this for me?

                  Also, along those lines of making a pointer volatile, I would like some
                  clarification. Consider this code abstract:

                  volatile char arr[10];
                  char arr2[10]
                  /* ... code that initialises both arrays ... */
                  if ( memcmp(&arr[3], &arr2[3], 1) ... )
                  /* etc. */

                  Does the deferencing of the first argument, arr, mean that memcmp is being
                  handed a non-volatile type (which is, I believe, the principle behind Chris
                  Torek's suggestion)?

                  --
                  Martin



                  Comment

                  • Ben Pfaff

                    #10
                    Re: Defeating Optimisation for memcmp()

                    "Martin" <martin.o_brien @[no-spam]which.netwrites :
                    I have two related questions. According to K&R2:
                    >
                    "Except that it should diagnose explicit attempts to change 'const'
                    objects, a compiler may ignore these qualifiers."
                    This statement is given in the context of qualifiers on types,
                    not qualifiers on pointers. I think that this is intended to
                    mean that the compiler is not obligated to store const objects in
                    read-only memory, and that it is not obligated to put volatile
                    objects in a special section of memory either.
                    --
                    Ben Pfaff

                    Comment

                    • Martin

                      #11
                      Re: Defeating Optimisation for memcmp()

                      "Ben Pfaff" <blp@cs.stanfor d.eduwrote in message
                      news:87tzngiqgw .fsf@blp.benpfa ff.org...
                      "Martin" <martin.o_brien @[no-spam]which.netwrites :
                      >
                      >I have two related questions. According to K&R2:
                      >>
                      > "Except that it should diagnose explicit attempts to change 'const'
                      >objects, a compiler may ignore these qualifiers."
                      >
                      This statement is given in the context of qualifiers on types,
                      not qualifiers on pointers. I think that this is intended to
                      mean that the compiler is not obligated to store const objects in
                      read-only memory, and that it is not obligated to put volatile
                      objects in a special section of memory either.
                      Are you saying that the quote from K&R2 above does not apply in these
                      instances?

                      int i, *const cpi = &i;
                      const int *pci;

                      --
                      Martin



                      Comment

                      • Ben Pfaff

                        #12
                        Re: Defeating Optimisation for memcmp()

                        "Martin" <martin.o_brien @[no-spam]which.netwrites :
                        "Ben Pfaff" <blp@cs.stanfor d.eduwrote in message
                        news:87tzngiqgw .fsf@blp.benpfa ff.org...
                        >"Martin" <martin.o_brien @[no-spam]which.netwrites :
                        >>
                        >>I have two related questions. According to K&R2:
                        >>>
                        >> "Except that it should diagnose explicit attempts to change 'const'
                        >>objects, a compiler may ignore these qualifiers."
                        >>
                        >This statement is given in the context of qualifiers on types,
                        >not qualifiers on pointers. I think that this is intended to
                        >mean that the compiler is not obligated to store const objects in
                        >read-only memory, and that it is not obligated to put volatile
                        >objects in a special section of memory either.
                        >
                        Are you saying that the quote from K&R2 above does not apply in these
                        instances?
                        >
                        int i, *const cpi = &i;
                        cpi is a const pointer to a non-const int. The compiler is not
                        obligated to put cpi into a read-only section.
                        const int *pci;
                        pci is a non-const pointer to a const int. The compiler may not
                        put pci into a read-only section.
                        --
                        char a[]="\n .CJacehknorstu" ;int putchar(int);in t main(void){unsi gned long b[]
                        ={0x67dffdff,0x 9aa9aa6a,0xa77f fda9,0x7da6aa6a ,0xa67f6aaa,0xa a9aa9f6,0x11f6} ,*p
                        =b,i=24;for(;p+ =!*p;*p/=4)switch(0[p]&3)case 0:{return 0;for(p--;i--;i--)case+
                        2:{i++;if(i)bre ak;else default:continu e;if(0)case 1:putchar(a[i&15]);break;}}}

                        Comment

                        • CBFalconer

                          #13
                          Re: Defeating Optimisation for memcmp()

                          Ben Pfaff wrote:
                          "Martin" <martin.o_brien @[no-spam]which.netwrites :
                          >
                          >I have two related questions. According to K&R2:
                          >>
                          >"Except that it should diagnose explicit attempts to change
                          >'const' objects, a compiler may ignore these qualifiers."
                          >
                          This statement is given in the context of qualifiers on types,
                          not qualifiers on pointers. I think that this is intended to
                          mean that the compiler is not obligated to store const objects
                          in read-only memory, and that it is not obligated to put
                          volatile objects in a special section of memory either.
                          The following was my attempt to test incrementing of a void*. I
                          think I can imagine situations where this ability would be useful
                          to bypass pointer incrementation. BTW, cc is shorthand for:
                          gcc -W -Wall -ansi -pedantic
                          and accesses gcc 3.2.1.

                          [1] c:\c\junk>cat junk.c
                          #include <stdio.h>

                          int main(void) {
                          void *p, *pb;
                          char by;

                          p = &by;

                          pb = p++;
                          if (pb == p) puts("++ uses sizeof void* == 0");
                          else puts("No luck here");
                          return 0;
                          }

                          [1] c:\c\junk>cc junk.c
                          junk.c: In function `main':
                          junk.c:9: warning: wrong type argument to increment

                          [1] c:\c\junk>a
                          No luck here

                          --
                          Chuck F (cbfalconer at maineline dot net)
                          <http://cbfalconer.home .att.net>
                          Try the download section.



                          --
                          Posted via a free Usenet account from http://www.teranews.com

                          Comment

                          • Ben Pfaff

                            #14
                            Re: Defeating Optimisation for memcmp()

                            CBFalconer <cbfalconer@yah oo.comwrites:
                            Ben Pfaff wrote:
                            >"Martin" <martin.o_brien @[no-spam]which.netwrites :
                            >>
                            >>I have two related questions. According to K&R2:
                            >>>
                            >>"Except that it should diagnose explicit attempts to change
                            >>'const' objects, a compiler may ignore these qualifiers."
                            >>
                            >This statement is given in the context of qualifiers on types,
                            >not qualifiers on pointers. I think that this is intended to
                            >mean that the compiler is not obligated to store const objects
                            >in read-only memory, and that it is not obligated to put
                            >volatile objects in a special section of memory either.
                            >
                            The following was my attempt to test incrementing of a void*. I
                            think I can imagine situations where this ability would be useful
                            to bypass pointer incrementation. [...]
                            I am struggling to understand how this is anything but a non
                            sequitur. Can you explain?
                            --
                            Ben Pfaff

                            Comment

                            • Dann Corbit

                              #15
                              Re: Defeating Optimisation for memcmp()


                              "Ben Pfaff" <blp@cs.stanfor d.eduwrote in message
                              news:87bq9oryaw .fsf@blp.benpfa ff.org...
                              CBFalconer <cbfalconer@yah oo.comwrites:
                              >
                              >Ben Pfaff wrote:
                              >>"Martin" <martin.o_brien @[no-spam]which.netwrites :
                              >>>
                              >>>I have two related questions. According to K&R2:
                              >>>>
                              >>>"Except that it should diagnose explicit attempts to change
                              >>>'const' objects, a compiler may ignore these qualifiers."
                              >>>
                              >>This statement is given in the context of qualifiers on types,
                              >>not qualifiers on pointers. I think that this is intended to
                              >>mean that the compiler is not obligated to store const objects
                              >>in read-only memory, and that it is not obligated to put
                              >>volatile objects in a special section of memory either.
                              >>
                              >The following was my attempt to test incrementing of a void*. I
                              >think I can imagine situations where this ability would be useful
                              >to bypass pointer incrementation. [...]
                              >
                              I am struggling to understand how this is anything but a non
                              sequitur. Can you explain?
                              A void pointer has no stride and cannot be incremented. I find the sentence
                              very difficult to parse.



                              --
                              Posted via a free Usenet account from http://www.teranews.com

                              Comment

                              Working...