On Wed, 28 Jun 2017 11:21:21 +0100
James Harris <
james.h...@nospicedham.gmail.com> wrote:
> On 28/06/2017 04:31, Rod Pemberton wrote:
> > On Wed, 28 Jun 2017 01:42:31 +0100
> > James Harris <
james.h...@nospicedham.gmail.com> wrote:
> >> On 27/06/2017 22:48, Rod Pemberton wrote:
> >>> On Tue, 27 Jun 2017 09:51:06 +0100
> >>> James Harris <
james.h...@nospicedham.gmail.com> wrote:
> >>> I kept the temporaries (local
> >>> variables) to encourage the compiler to place them in registers.
> >>
> >> Looking at the generated code, below, it picks up ESI and EDI from
> >> the stack - i.e. they are p0 and p1. It keeps them unchanged and it
> >> uses them in the body of the code. x and y would, therefore, seem
> >> to be redundant. (Assuming your p0 and p1 could be declared as
> >> char *).
> >
> > Maybe. I've not confirmed for this code whether that's needed or
> > not.
> >
> > I've generally found with GCC that using auto's "encourages" GCC to
> > keep values in registers, instead of reloading them from the stack
> > or memory. Sometimes this is optimized away, i.e., unnecessary as
> > you've pointed out may be the situation. But, it ensures that GCC
> > doesn't generate bad code by default, as is normal. E.g., GCC is
> > exceptionally bad at selecting when to use 8-bit registers. It can
> > take plenty of declaration type juggling to fix this.
>
> Do you look at generated code much?
(This question/argument reminds me of someone on c.l.c., whom I'm not
fond of. He repeatedly and wrongly argues that there is no reason or
need for a C programmer to ever look at the assembly. AISI, a
programmers' job is not only to program, but to verify the accuracy of
the code they produce.)
Yes, I do look at generated code, but not for everything. In many
cases, it's not required to so, if you're reasonably assuaged that the
compiler is generating correct code. I look maybe only 10% to 25% of
the time. I do so especially when speed or size is an issue, such as
for the recent threads here, or for say a hex dump or text conversion
program, etc. I also look if I want something to be stored in
registers. GCC is bad about this. And, there are many ways to express
things in C and certain combinations produce much better assembly when
optimized. E.g., you probably noticed that I used a while(1) with
break instead of a for(). Many times, these simple loops optimize
better than for(), do-while(), or while().
> I hardly ever look at the code it has emitted. I can't think why
> anyone would
See above.
Also, recent versions of GCC were reported to optimize away legitimate
code in some situations. I happened to see one instance on Linux when
compiling for this thread. I had the "if-break" statement in a
different location, and even though the C code was correct, GCC
"optimized", i.e., deleted, away the rest of my loop ... This was
noticed upon execution, and then confirmed by the missing assembly.
> - unless you think the compiler will produce faulty code.
No, I don't usually do it for that. That'll usually show up with
testing, or execution. If it's a primitive C compiler, I may look
for a while just to make sure the code is correct. If there is
something complicated or obfuscated in the code, I might take a look
too, e.g., pointer chains, typedef obfuscation, etc.
> And if you did, would you not ditch that compiler or file a bug
> report?
I can't do that.
I can update GCC for Linux, if there is an issue. I can't update GCC
for DOS, as later versions of DJGPP have changes intended for NTVDM or
a DOS console in Windows. These changes aren't compatible with true
DOSes unlike my older DJGPP version. Although newer versions of
OpenWatcom compiler are available, I can't update OpenWatcom, yet, as
the newer versions have some changes which require "porting" the C code.
> > I'm not sure if that creates a register dependency or not, as the
> > register being accessed is not modified. I would assume that it
> > wouldn't, but it might.
>
> Effectively, register reads can happen in parallel. There are three
> "data hazards" but all involve writing:
>
https://en.wikipedia.org/wiki/Hazard_(computer_architecture).
The keyword is "can". The question is "Do they?" and on which x86
processors? All, none, some, newer? Do any have bugs? Etc. Without
testing, I don't know.
> > Do you see a way to only check one string for the EOS without
> > potentially reading past the end of the other string? If so, I'd be
> > interested in seeing that solution.
>
> AISI, we are scanning the two strings for the first substantive
> difference. As soon as we find such a difference we exit. If that
> difference is that one string ends before the other then the normal
> algorithm will exit. For example, if 0 means the binary zero at the
> end of the string
>
> any0
> anybody0
>
> The normal algorithm will stop when it compares 0 and b.
> But the normal algorithm wouldn't stop if the two strings were
> identical. It would run on past the strings. Therefore we need a test
> to prevent that happening - and testing either for a terminating zero
> is enough. Hence I think we only need to check on of the strings for
> the terminating zero.
I don't know what you mean by "normal algorithm".
The original algorithm I posted will break out on any mismatch, whether
a '\0' is involved or not. So, we don't need to check in that
situation. It will also stop when the strings are identical by
breaking out by matching one '\0'. Since both strings are identical,
both have '\0', we only need to check one of them for '\0'.
The xor algorithm I posted will break if there is a mismatch with an
xor difference other than 0x20. So, the question is are there
situations where there can be a '\0' character which follows the
"continue" statements? The answer to that is: "Yes." If both are
'\0', i.e., an exact string match, we must check at least one of them.
That is the first "continue" statement. If one character is '\0' 0x00
and the other is space 0x20, then we must check both of them. That is
the second "continue" statement. E.g., at least one of these examples
will continue reading past the end of the shorter "Hello" string, if we
don't check both strings for the '\0':
/* xor difference of 0x20, x='\0' y=' ' */
Hello\0
Hello World!\n\0
/* xor difference of 0x20, x=' ' y='\0' */
Hello World!\n\0
Hello\0
If we only check x for '\0', we'll miss y being '\0'. Variable t in the
algorithm doesn't have sufficient information to detect this. So, we
must check both strings for '\0' when there is an xor difference of
0x20.
You stated you have TWO non-alphabetic characters with an XOR of 0x20.
For '!' (0x21), the only non-alphabetic "character" with an XOR of 0x20
is the non-printable SOH or Ctrl-A (0x01). So, let's take two
non-alphabetic visible graphics characters instead. For '[' lower
returns '[', and for '{' lower returns '{'. Neither is lower-cased.
Neither is converted to a value that won't result in an XOR difference
of 0x20 between the two, which would cause the XOR to fail. Also, '{'
isn't converted to '[', which would allow XOR to to match. If lower()
doesn't cause a failure or create an acceptance, how does this help? ...
JH> the XOR of two non-alphabetic values could also result in 0x20,
True, if they're graphics characters, not alpha-numeric. But, the XOR
routine wasn't designed for use with graphics characters. Remember the
XOR suggestion was for Ben, not Rick. Ben didn't need graphics
characters. He only needed a routine for alphabetic and numeric
characters. No graphic. You asked about the XOR routine as suggested
to Ben, but did so in Rick's thread. ...
JH> t being 0x20 means that they /might/ match. You would still need to
JH> use lower() (or lower[]) to check.
If you wanted this to work for graphics characters, you'd need an
additional check to exclude or detect the graphics characters, as I
stated previously. This could be a call to isgraph() or !isalnum() or
even x==y, etc. Calling lower() doesn't help, unless you replace the
XOR==0x20 check with "lower(x)==lower(y)". In which case, we're
basically back to the earlier non-XOR routine, since we aren't using
the XOR anymore which you specifically requested. ...
> > (Remember, despite what those self-proclaimed "C experts" over on
> > c.l.c. say, there are *NO* arrays in C. C only has an array
> > declaration that allocates space for storage and declares a pointer
> > to it. All use of "arrays" in the C language are actually use of
> > the "subscript operator" [] . The subscript operator works with
> > any pointer to a valid type, i.e., non-void, and an offset. Hence,
> > no arrays in C.)
>
> OT for this newsgroup but contrast these two:
>
> char a[10];
> char *p;
>
> When passed to a function they would both go as pointers
>
> f(a, p);
When used with the subscript operator [], both are pointers:
char c;
c=a[1]; /* 1 is offset. [] is subscript operator. a is pointer */
c=p[1]; /* 1 is offset. [] is subscript operator. p is pointer */
Where is the array? ... No array here.
The subscript operator [] doesn't accept arrays as a parameter. It
only accepts a pointer to a type and an offset. The operation of the
subscript operator is defined in all the C standards this way,
including early C documents and articles. This nullifies the entire
argument that an array, such as a[], *ONLY* decays into a pointer when
passed into a function as the specifications state. An array is never
an array in the first place, except when declared. Afterwards, it's
always a pointer, but appears otherwise due to syntax.
> and in the function they would both act as pointers to char.
Not act. Are.
> But in the original they would be different in a number of ways.
> For example,
>
> sizeof(a)
> sizeof(p)
>
> would not be the same.
sizeof() is rather "special". It provides access to information only
known by the compiler at compile time, e.g., symbol tables. IIRC, it's
rather bizarre in some situations and incomplete in others. I generally
don't need it for anything, but do use it to check type sizes on
different platforms.
So, you're arguing that a "faulty", specialty, customized, low-use
function, justifies claiming that arrays exist in the C language, other
than simply for their declaration and allocation? If so, I
wholeheartedly disagree.
The arrays' type *CANNOT* be checked throughout the C code, because
there are no arrays in C. Everywhere you think there is an array in C
code, except for the array declaration, there is a only a (non-void)
pointer, an offset, and a subscript operator, or just a (non-void)
pointer. Never is there an array, except the declaration. That's just
there to allocate space.
(This is apparently a hard concept for most to grasp, as it almost
always sparks an intense argument or flame war.)
Rod Pemberton
--