Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

capabilities of C vs Ada

7 views
Skip to first unread message

Robert Dewar

unread,
Dec 9, 1993, 12:07:24 AM12/9/93
to
Mike Livorsi said:
.
>In article <2e5151$s...@schonberg.cs.nyu.edu>, de...@cs.nyu.edu
>(Robert Dewar) writes:
>>
.
>>Of course you can use Ada constructs that don't exist in C to do things
>>that you can't do in C, but any C program can be duplicated in a full
>>featured Ada compiler. In particular, for the example of GCC and GNAT,
>>I am quite sure that any C program can be translated into Ada in such a
>>way that the generated code from GCC will be bit for bit identical. Now
>>of course one has to write rather low level Ada to do that, which you
>>only want to do when you need to, but the idea that there is something
>>here that C can do that Ada can't is technical nonsense (that's the
>>less polite term for "technically unsupportable", just to emphasize
>>that you still haven't proved your point, at least as far as I am
>>concerned!)
.
>I take it that the "full featured" Ada compiler you are talking about fully
>supports "pragma interface (C, .... )". Let's review a few of the
>capabilities of C which are not duplicated in Ada:
.
.
>1. POINTERS
.
>While an access type has some of the capabilities that a C pointer, it is very
>lacking. Consider the following code excerpt:
.
> typdef struct {
> int item1;
> int item2;
> int item3;
> } Atype;
> int i;
> Atype aa[10000], *aa_ptr;
> aa_ptr = aa;
> for (i=0 ; i<10000 ; i++, aa++) {
> aa.item1 = 5;
> aa.item2 = 6;
> aa.item3 = 7;
> }
.
>Notice that the resulting code will not require ANY array index calculations
>which is bound to contain a multiply by 3 (since there are three items in this
>structure). The "Ada equivalent" would require array indexes. It would take
>a very intelligent Ada compiler to be able to figure out an optimization that
>avoids array calculations (or is this to be expected from any "full featured"
>Ada compiler?).
.
Any reasonable compiler should take the loop:
.
. for I in 0 .. 9999 loop
. aa.item1 := 5;
. aa.item2 := 6;
. aa.item3 := 7;
. end loop;
.
and do elementary strength reduction and test replacement to obtain
identical code to what is generated for the C loop above. This is hardly
something that requires a dynamite compiler (strength reduction in a loop
of this kind is among the most elementary compiler optimizations)
.
To demonstrate this, I took the C code above and first tried to make
sense out of it (it's got a lot of nonsense in it as presented, including
the attempt to increment an array), but one can guess that what was
intended was something like:
.
. c1 () {
. typedef struct {
. int item1;
. int item2;
. int item3;
. } Atype;
. int i;
. Atype aa[10000], *aa_ptr;
. aa_ptr = aa;
. for (i=0 ; i<10000 ; i++, aa_ptr++) {
. (*aa_ptr).item1 = 5;
. (*aa_ptr).item2 = 6;
. (*aa_ptr).item3 = 7;
. }
. }
.
compiling for the 386, GCC generates for the loop:
.
. L5:
. movl $5,(%eax)
. movl $6,4(%eax)
. movl $7,8(%eax)
. incl %edx
. addl $12,%eax
. cmpl $9999,%edx
. jle L5
.
The corresponding Ada program is something like:
.
. procedure A1 is
. type Atype is record
. item1 : integer;
. item2 : integer;
. item3 : integer;
. end record;
.
. i : integer;
.
. aa : array (0 .. 9999) of Atype;
.
. begin
. for i in 0 .. 9999 loop
. aa (i).item1 := 5;
. aa (i).item2 := 6;
. aa (i).item3 := 7;
. end loop;
. end A1;
.
compiling for the 386 using GNAT, we get for the loop:
.
. L7:
. movl $5,(%edx,%eax)
. movl $6,4(%eax,%edx)
. movl $7,8(%eax,%edx)
. addl $12,%eax
. cmpl $119988,%eax
. jle L7
.
This is actually one instruction shorter than the C code (and there is
certainly no trace of multiplies by 3 or any such nonsense). However,
that's not quite fair, the C is written in a peculiar way, most C
programmers would not introduce the unnecessary variable I, they would
simply write:
.
. c1 () {
. typedef struct {
. int item1;
. int item2;
. int item3;
. } Atype;
. Atype aa[10000], *aa_ptr;
. for (aa_ptr = aa ; aa_ptr<&aa[10000] ; aa_ptr++) {
. (*aa_ptr).item1 = 5;
. (*aa_ptr).item2 = 6;
. (*aa_ptr).item3 = 7;
. }
. }
.
and then they would get for the loop:
.
. L5:
. movl $5,(%eax)
. movl $6,4(%eax)
. movl $7,8(%eax)
. addl $12,%eax
. cmpl %ebp,%eax
. jb L5
.
i.e. essentially the same six instruction as in the Ada case
.
Bottom line, this is a pretty clear demonstration that there is no need
to muck around with low level pointer mucking to get decent code, and
acts as a nice example of the general principle that you get pretty much
the same code out of Ada and C compilers, or at least you should do. If
you don't, it's a commentary on the compilers not on the language.
.
>2. Variable argument lists
>
>Ada simply lacks this capability altogether. The familiar "printf" is a fine
>example of a variable argument list:
>
> printf ("Hello %s. You have %d messages.\n", name, num_messages);
.
Indeed you can't do this directly in Ada, because it is fundamentally type
unsafe. As most C programmers know, printf is unable to do any type checking
and can generate most interesting results if the types get messed up. If you
have a homogenously typed variable length list, then the aggregate notation
in Ada gives you what you want. If you absolutely MUST have a type unsafe
heterogenous list, you could do it in Ada by having an array of Address values
for the objects, and then dereferncing these addresses via unchecked conversion
in Ada 83, or the appropriate routines in System in Ada 9X. Yes the code would
look pretty horrible, but the Ada philosophy is that if you want to do horrible
type unsafe things, it is quite appropriate for the code to reflect this, rathe
than making it too easy to sink to such a level.
.
>3. Bitwise operators
>
>Bitwise ANDs, ORs, shifts, etc. are trivial in most other languages. Ada
>lacks anything standard here. I know it allows to to layout records in a
>bitwise fashion (so does C by the way), but this just isn't the same.
>Usually, one must rely on an "implementation defined" package or some other
>non-portable way to do these operations. In any case, these operations
>are inherant in a C compiler and it can optimize expressions using bitwise
>operations.
.
In Ada-83 bitwise operations are certainly available between packed bit
strings as in:
.
. type Bits is array (0 .. 31) of Boolean;
. pragma Pack (Bits);
.
. A,B,C : Bits;
. A := B or C xor (B and C);
.
If you need to do logical operations on bits, then you need to define
unchecked conversions between bit strings defined in this way and
integer types. Works fine, and portably (assuming your compiler is
for the full language and supports all of chapter 13 properly).
.
Note also that in Ada 9X, modular types allow direct bitwise operations
on unsigned integer values in the C style (let's hope people don't misuse
this low level feature!)
.
Finally, where does this "trivial in most other languages come from". Of
the languages that I do use or have in the past routinely used (Ada,
Fortran, C, COBOL, Algol-68, SNOBOL4), only C has this capability (well
I guess I didn't include assembler :-)
.
.
>4. Xwindows based applications
>
>This one is my biggest beef. Why is there no STANDARD API for Ada yet? I
>know that there are many API's for Xwindows in Ada that are available; but
>that's the whole problem! It is currently IMPOSSIBLE to write a portable
>Xwindows application in Ada.
.
Fair enough, but absolutely nothing to do with the languages, merely with
what bindings happen to have been written. I am the first to understand
the importance of bindings in Ada, as I hope I have made clear in many
posts in the past.
.
>Mike

Robert

Adam Beneschan

unread,
Dec 9, 1993, 3:49:17 PM12/9/93
to
de...@cs.nyu.edu (Robert Dewar) writes:

>>2. Variable argument lists
>>
>>Ada simply lacks this capability altogether. The familiar "printf" is a fine
>>example of a variable argument list:
>>
>> printf ("Hello %s. You have %d messages.\n", name, num_messages);
>.
>Indeed you can't do this directly in Ada, because it is fundamentally type
>unsafe. As most C programmers know, printf is unable to do any type checking
>and can generate most interesting results if the types get messed up. If you
>have a homogenously typed variable length list, then the aggregate notation
>in Ada gives you what you want. If you absolutely MUST have a type unsafe
>heterogenous list, you could do it in Ada by having an array of Address values
>for the objects, and then dereferncing these addresses via unchecked conversion
>in Ada 83, or the appropriate routines in System in Ada 9X. Yes the code would
>look pretty horrible, but the Ada philosophy is that if you want to do horrible
>type unsafe things, it is quite appropriate for the code to reflect this, rathe
>than making it too easy to sink to such a level.
>.

I actually wrote and used a package that allowed me to create a
sort-of heterogeneous list and pass it to an sprintf-like function.
It looked something like this:

package SPRINTF_PACKAGE is

type ITEM_TYPE is (I_NONE, I_INTEGER, I_FLOAT, I_CHARACTER, I_STRING);
type ITEM (ITYPE : ITEM_TYPE := I_NONE) is record
case ITYPE is
when I_NONE => null;
when I_INTEGER => i : integer;
when I_FLOAT => f : float;
when I_CHARACTER => c : character;
when I_STRING => s : string_pointer;
end case;
end record;

type ITEM_ARRAY is array (natural range <>) of ITEM;

function V (i : integer) return ITEM;
function V (f : float) return ITEM:
function V (c : character) return ITEM;
function V (s : string) return ITEM;

function SPRINTF (format : in string;
items : in ITEM_ARRAY) return string;

end SPRINTF_PACKAGE;

Then I could code stuff like

TEXT_IO.PUT_LINE
(SPRINTF ("Hello %s. You have %-3d messages.",
(V (name), V (num_messages))));

which is almost as convenient as the C version---you just have to add
a few parentheses and some V's. Also, this version of SPRINTF does
type checking for you (by checking the ITYPE of each item), which
neither the C functions nor Robert Dewar's 'ADDRESS suggestion can do.

The drawbacks are that it's a little less efficient than passing
addresses, since the V function has to be called. String items aren't
handled too well (in the version I wrote, the string version of V
simply allocated a copy of the string on the heap; there are better
ways to handle strings). Also, you're limited as to what types you
can pass to SPRINTF. If you declare your own integer type, an object
of that type must be converted to INTEGER before it's passed to
SPRINTF. (9X should alleviate those problems; it should also allow
you to pass objects of ANY type, by writing your own formatting
routine for that type. You could then, for example, use SPRINTF on
complex numbers, which you can't do in C.)

I try not to use this version of SPRINTF any more; now, I prefer code
like:

TEXT_IO.PUT_LINE
("Hello "
& name
& ". You have "
& FORMAT_INTEGER (num_messages, base => 10, width => 3,
justify => left)
& ".");

(When I was working in C, one of the most annoying things was that you
can't write functions that return strings, and there's no string
concatenation operator, so you can't write code like this.)

-- Adam

Mike Livorsi

unread,
Dec 9, 1993, 7:03:31 PM12/9/93
to
In article <2e6bqc$1...@schonberg.cs.nyu.edu>, de...@cs.nyu.edu (Robert Dewar) writes:

>>1. POINTERS

>Any reasonable compiler should take the loop:
>..

>.. for I in 0 .. 9999 loop
>.. aa.item1 := 5;
>.. aa.item2 := 6;
>.. aa.item3 := 7;
>.. end loop;
>..


>and do elementary strength reduction and test replacement to obtain
>identical code to what is generated for the C loop above. This is hardly
>something that requires a dynamite compiler (strength reduction in a loop
>of this kind is among the most elementary compiler optimizations)

>The corresponding Ada program is something like:
>..
>.. procedure A1 is
>.. type Atype is record
>.. item1 : integer;
>.. item2 : integer;
>.. item3 : integer;
>.. end record;
>..
>.. i : integer;
>..
>.. aa : array (0 .. 9999) of Atype;
>..
>.. begin
>.. for i in 0 .. 9999 loop
>.. aa (i).item1 := 5;
>.. aa (i).item2 := 6;
>.. aa (i).item3 := 7;
>.. end loop;
>.. end A1;
>..


>compiling for the 386 using GNAT, we get for the loop:
>..

>.. L7:
>.. movl $5,(%edx,%eax)
>.. movl $6,4(%eax,%edx)
>.. movl $7,8(%eax,%edx)
>.. addl $12,%eax
>.. cmpl $119988,%eax
>.. jle L7
>..

I was surprised that someone carried it to this level! GNAT sure beats DEC
Ada 3.0 for the VAX (which I consider to be a very mature "dynamite"
compiler). It yields the following with optimization:

Line 13: CLRL R2
Line 14: MULL3 S^#3,R2,AP
: MOVL S^#5,L^-120012(FP)[AP]
Line 15: MOVL S^#6,L^-120008(FP)[AP]
Line 16: MOVL S^#7,L^-120004(FP)[AP]
Line 17: INCL R2
: CMPL R2,I^#9999
: BLEQ A1.LOOP$13.%LINE 14

While it did recognize that the computed index could be reused, it was unable
to remove the multiply by 3 for each pass through the loop.


>>3. Bitwise operators
>>
>>Bitwise ANDs, ORs, shifts, etc. are trivial in most other languages. Ada
>>lacks anything standard here. I know it allows to to layout records in a
>>bitwise fashion (so does C by the way), but this just isn't the same.
>>Usually, one must rely on an "implementation defined" package or some other
>>non-portable way to do these operations. In any case, these operations
>>are inherant in a C compiler and it can optimize expressions using bitwise
>>operations.

>..


>Finally, where does this "trivial in most other languages come from". Of
>the languages that I do use or have in the past routinely used (Ada,
>Fortran, C, COBOL, Algol-68, SNOBOL4), only C has this capability (well
>I guess I didn't include assembler :-)

"Other languages" I was refering to include:
BASIC (Microsoft variants)
C
Assembly
FORTRAN (I differ with you here, but every FORTRAN compiler I have used
DOES support .and. and .or. in a bitwise fashion; shift is
usually provided as well, but it's not standardized)

>
>Robert
>

Mike

........................................................................
Michael Livorsi
F/A-18E/F Flight Controls Software
McDonnell Douglas Corporation
liv...@fcws11.mdc.com

Bevin R. Brett

unread,
Dec 10, 1993, 1:27:09 PM12/10/93
to

In article <1993Dec9.1...@desire.wright.edu>, livorsi@FCWS11 (Mike Livorsi) writes...

>>compiling for the 386 using GNAT, we get for the loop:
>>..
>>.. L7:
>>.. movl $5,(%edx,%eax)
>>.. movl $6,4(%eax,%edx)
>>.. movl $7,8(%eax,%edx)
>>.. addl $12,%eax
>>.. cmpl $119988,%eax
>>.. jle L7
>>..
>
>I was surprised that someone carried it to this level! GNAT sure beats DEC
>Ada 3.0 for the VAX (which I consider to be a very mature "dynamite"
>compiler). It yields the following with optimization:
>
>Line 13: CLRL R2
>Line 14: MULL3 S^#3,R2,AP
> : MOVL S^#5,L^-120012(FP)[AP]
>Line 15: MOVL S^#6,L^-120008(FP)[AP]
>Line 16: MOVL S^#7,L^-120004(FP)[AP]
>Line 17: INCL R2
> : CMPL R2,I^#9999
> : BLEQ A1.LOOP$13.%LINE 14
>
>While it did recognize that the computed index could be reused, it was unable
>to remove the multiply by 3 for each pass through the loop.


I tried putting the following example through our DEC Ada for OpenVMS AXP
compiler, was very embarrassed by the result, spent some time this morning
fixing the glitches that were inhibiting optimisation, and now have a compiler
that does the following...

1 procedure Eg is
2 type R is
3 record
4 I,J,K : Integer;
5 end record;
6 X : array(1..10) of R;
7 begin
8 for I in X'range loop
9 X(I).I := 0;
10 X(I).J := 1;
11 X(I).K := 2;
12 end loop;
13 end Eg;

EG::
STQ R31, -16512(SP)
LDA SP, -128(SP)
LDA R1, -4(SP)
CLR I
MOV 2, R0
MOV 1, R17
L$2:
STL R31, 12(R1)
ADDL I, 5, I
STL R17, 16(R1)
ADDQ I, -10, R19
STL R0, 20(R1)
STL R31, 24(R1)
STL R17, 28(R1)
STL R0, 32(R1)
STL R31, 36(R1)
STL R17, 40(R1)
STL R0, 44(R1)
STL R31, 48(R1)
STL R17, 52(R1)
LDA R1, 60(R1)
STL R0, -4(R1)
STL R31, (R1)
STL R17, 4(R1)
STL R0, 8(R1)
BLT R19, L$2
LDA SP, 128(SP)
RET R26

Which differs from the GNAT example in that the loop has been unrolled
five times, and there is some fiddling around with 'I' that I have yet to
understand.

[My loop went from 1..10 instead of 0..9999, it makes no difference
to the example].

The VAX Ada compiler uses a very old code generator, the VCG, that has not had
any significant work put into it over the last ten years. In particular it
doesn't have this kind of strength reduction because the multiplies didn't use
to be much slower than the adds.

/Bevin

DEC Ada team

Robert Dewar

unread,
Dec 11, 1993, 11:15:43 AM12/11/93
to
I think it quite surprising that Dec does not get the multiply out of the
loop here, are you sure you used all optimization options? This really is
an elementary example of strength reduction, many of the student compilers
turned in for a beginning compilers course could do this well on a simple
example like this. There really couldn't be a much simpler example! So,
despite the curious counter-example, I stick to my original point there,
any decent compiler should get the multiply out of this loop -- and you
shouldn't get the impression that the GCC code is some amazing feat. In
fact, as Richard Kenner points out, it is a little disappointing that there
was the extra instruction in the first loop (the one dealing with the junk
variable I).

Regarding the claim that "the FORTRAN's I use all allow bitwise logical
operations", this claim can of course be true (for some particular person
and their experience), but I hope everyone realizes that the Fortran standard
definitely does NOT support this usage.

By this standard, I wouldn't be surprised if nearly all Ada compilers support
bitwise logical operations using the ordinary And/Or/Not etc on Booleans.
Try:

procedure Integer_Or (A,B : Integer) return Integer is


Ooops, some kind of glitch, let's try that again:

with Unchecked_Conversion;
function Integer_Or (A,B : Integer) return Integer is
type B is new Boolean;


Oh heck I'll have to start a new message, my editor is in some peculiar stateZit

Robert Dewar

unread,
Dec 11, 1993, 11:24:43 AM12/11/93
to
OK, let's try that again

with Unchecked_Conversion;
function Integer_And (A,B : Integer) return Integer is
. type MyB is new Boolean;
. for MyB'Size use Integer'Size;
. function ItoB is new Unchecked_Conversion (Integer, Boolean);
. function BtoI is new Unchecked_Conversion (Boolean, Integer);
begin
. return BtoI (ItoB (A) and ItoB (B));
end;

I think that's about as likely to work on an arbitrary Ada compiler as
the suggested approach (use .AND.) is to work on an arbitrary Fortran
compiler.

Furthermore, I would expect a decent Ada compiler to generate essentially
optimal code for this function. Unfortunately I can't try it on GNAT because
derived Boolean types are not implemented.

(Up to now we had thought this a non-critical feature, obviously, in view
of the important usage above, we must raise its priority :-) :-)

Robert Dewar

unread,
Dec 11, 1993, 11:31:36 AM12/11/93
to
Nice code example from Bevin, indeed loop unrolling is an additional
advantage here. I think it's the case that the C loop could equally well
be unrolled though, so it shouldn't be interpreted as an Ada advantage.
I guess actually it *is* a little harder to unroll the C loop, since you
need aliasing analysis to be sure that the pointer indirected stores are
not modifying the loop termination value, which in C, unlike the case in
Ada, is evaluated each time around the loop.

Actually this is a specific example of a general problem with C, the pointers
are a little too general, and when you see something like:

*x = y;

you need a lot of analysis, often very difficult to do in the interprocedural
case, to limit the implied damage of the assignment (on the face of it, this
assignment could destroy any variable of the appropriate type anywhere --
indeed if your C programmers expect aliasing via pointer casts to "work
properly", then it may be too restrictive to assume that only the evident
type is affected!

robin

unread,
Dec 11, 1993, 5:57:46 AM12/11/93
to
de...@cs.nyu.edu (Robert Dewar) writes:

>Mike Livorsi said:

>>2. Variable argument lists
>>
>>Ada simply lacks this capability altogether. The familiar "printf" is a fine
>>example of a variable argument list:
>>
>> printf ("Hello %s. You have %d messages.\n", name, num_messages);

>Indeed you can't do this directly in Ada, because it is fundamentally type unsafe.

---No, it's not someting that's "fundamentally type unsafe". It's perfectly
practical in BASIC, PL/I and COBOL, with appropriate type conversions.

As most C programmers know, printf is unable to do any type checking

---There's no reason why it couldn't.

>>3. Bitwise operators

>>Bitwise ANDs, ORs, shifts, etc. are trivial in most other languages. Ada
>>lacks anything standard here. I know it allows to to layout records in a
>>bitwise fashion (so does C by the way), but this just isn't the same.
>>Usually, one must rely on an "implementation defined" package or some other
>>non-portable way to do these operations. In any case, these operations
>>are inherant in a C compiler and it can optimize expressions using bitwise operations.

>In Ada-83 bitwise operations are certainly available between packed bit strings ...

>. type Bits is array (0 .. 31) of Boolean;
>. pragma Pack (Bits);

>. A,B,C : Bits;
>. A := B or C xor (B and C);

>If you need to do logical operations on bits, then you need to define


>unchecked conversions between bit strings defined in this way and
>integer types. Works fine, and portably

---but not "trivial".

>Finally, where does this "trivial in most other languages come from". Of
>the languages that I do use or have in the past routinely used (Ada,
>Fortran, C, COBOL, Algol-68, SNOBOL4), only C has this capability (well
>I guess I didn't include assembler :-)

---There's PL/I, as well as FORTRAN (90). [Incidentally, in PL/I, it's
unnecessary to define an array of bits--one can use bit strings. One can
also use arrays of bit strings.]


Robb Nebbe

unread,
Dec 15, 1993, 9:05:26 AM12/15/93
to
r...@goanna.cs.rmit.oz.au (robin) writes:

>de...@cs.nyu.edu (Robert Dewar) writes:
>>Indeed you can't do this directly in Ada, because it is fundamentally type unsafe.
>
>---No, it's not someting that's "fundamentally type unsafe". It's perfectly
>practical in BASIC, PL/I and COBOL, with appropriate type conversions.
>
>>As most C programmers know, printf is unable to do any type checking
>
>---There's no reason why it couldn't.

printf is a function and for it to do any type checking on its own it
would have to do that at run time. However, all type information has
been optimised away before we get to run time so there is no way printf
could do any type checking.

You could envisage the case where the C compiler parses the format
string and then uses this information to verify the number of arguments
and their types or possibly some other scheme where printf depends on
the complicity of the compiler, but on its own printf can't do any
serious type checking.

Furthermore the ramification of type safe variable argument lists would
be that the number of a function's parameters and their types would be
dependent on one (or more if we want to generalize) of the parameters
of the function. From a software engineering point of view this doesn't
strike me as a particularly brillant solution to the problem.

-Robb

Robert Firth

unread,
Dec 15, 1993, 1:32:21 PM12/15/93
to
In article <2en5j6$m...@disuns2.epfl.ch> ne...@di.epfl.ch writes:

>printf is a function and for it to do any type checking on its own it
>would have to do that at run time. However, all type information has
>been optimised away before we get to run time so there is no way printf
>could do any type checking.

I seem to recall that the Algol-68R implementation had an elegant
solution to the problem. The second parameter was a dynamically
sized array, each of whose components was a discriminated union
of all printable base types. (Yes, that does need structural type
equivalence to work without generics.) The procedure then checked
the format cues individually against the type tags in the unions.

Given that I/O is a pretty expensive thing to do anyway, the extra
runtime overhead wasn't all that great (one case statement and one
or two tests per parameter), and it preserved type safety.

Thomas M. Breuel

unread,
Dec 15, 1993, 4:19:06 PM12/15/93
to
In article <2en5j6$m...@disuns2.epfl.ch>, ne...@di.epfl.ch (Robb Nebbe) writes:
|> >>As most C programmers know, printf is unable to do any type checking
|> >
|> >---There's no reason why it couldn't.
|>
|> printf is a function and for it to do any type checking on its own it
|> would have to do that at run time. However, all type information has
|> been optimised away before we get to run time so there is no way printf
|> could do any type checking.

The ANSI C standard doesn't prohibit runtime type checks, it merely
doesn't mandate them. As a matter of fact, there are implementations
of C that perform the necessary runtime type checking.

|> You could envisage the case where the C compiler parses the format
|> string and then uses this information to verify the number of arguments
|> and their types or possibly some other scheme where printf depends on
|> the complicity of the compiler, but on its own printf can't do any
|> serious type checking.

GNU C will statically type check printf arguments when the format
string is known, which is by far the most common case.

Thomas.

Tom Quiggle

unread,
Dec 14, 1993, 12:40:39 PM12/14/93
to
In article <2ecrnf$a...@schonberg.cs.nyu.edu>,

Robert Dewar <de...@cs.nyu.edu> wrote:
>I think it quite surprising that Dec does not get the multiply out of the
>loop here, are you sure you used all optimization options?
>
> ...
> I stick to my original point there,
>any decent compiler should get the multiply out of this loop -- and you
>shouldn't get the impression that the GCC code is some amazing feat.

I too was surprised the DEC Ada didn't eliminate the multiplication.
For another data point, I ran the above code through the only two Ada
compilers I have available on this machine: the old TeleSoft
RISCAda/SPARC compiler (RIP), and the Verdix VADSworks compiler for the
68020. Both generated architecturally equivalent code as shown below.

>>The corresponding Ada program is something like:
>>..
>>.. procedure A1 is
>>.. type Atype is record
>>.. item1 : integer;
>>.. item2 : integer;
>>.. item3 : integer;
>>.. end record;
>>..
>>.. i : integer;
>>..
>>.. aa : array (0 .. 9999) of Atype;
>>..
>>.. begin
>>.. for i in 0 .. 9999 loop
>>.. aa (i).item1 := 5;
>>.. aa (i).item2 := 6;
>>.. aa (i).item3 := 7;
>>.. end loop;
>>.. end A1;
>>..

RISCAda/SPARC: (excess commentary deleted)

! 14: for i in 0 .. 9999 loop

LN4: set -0x1d4fc,%l0 !001c a0142304
add %fp,%l0,%l4 !0020 a8078010
set 0x270f,%l1 !0028 a214630f
mov %g0,%l2 !002c a4100000
mov %l4,%l3 !0030 a6100014
L4:
! 15: aa (i).item1 := 5;
! 16: aa (i).item2 := 6;
! 17: aa (i).item3 := 7;
!
! 18: end loop;
!
LN8:
LN9: set 5,%l0 !0034 a0102005
st %l0,[%l3] !0038 e024c000
set 6,%l0 !003c a0102006
st %l0,[%l3+4] !0040 e024e004
set 7,%l0 !0044 a0102007
st %l0,[%l3+8] !0048 e024e008
add %l2,1,%l2 !004c a404a001
cmp %l2,%l1 !0050 80a48011
ble L4 !0054 04bffff8
add %l3,0xc,%l3 !0058 a604e00c

It's a shame the code generator did't choose to assign the numeric literals
5, 6, and 7 to local registers (say %l5, %l6, and %l7) and hoist their
initialization out of the loop. But then again, the Sun C compiler didn't
do that for the equivalent C code in Robert Dewar's post.


VADSworks/68020:

14 for i in 0 .. 9999 loop
001a: moveq.l #00, d0
001c: movea.l a6, a0
001e: adda.l #-01d4c4, a0
0024: adda.l #-0c, a0

15 aa (i).item1 := 5;
002a: adda.l #0c, a0
0030: move.l #05, (a0)

16 aa (i).item2 := 6;
0036: move.l #06, (04,a0)

17 aa (i).item3 := 7;
003e: move.l #07, (08,a0)

18 end loop;
0046: addq.l #01, d0
0048: cmpi.l #0270f, d0
004e: ble.b -026 -> 02a


The conclusion I derive from this experiment is that Robert is correct.
Modern Ada compilers do indeed generate code on par with modern C
compilers -- even without resorting to 'C' pointer trickery as referenced
in the original post on this thread.

--
Tom Quiggle t...@visicom.com quig...@ajpo.sei.cmu.edu
VisiCom Laboratories Inc.
San Diego, CA 92121

Voice: (619) 457-2111 ex 231 Fax: (619) 457-0888

An American's a person who isn't afraid to criticize the President but
is always polite to traffic cops.

-- the fortune program


Robert Dewar

unread,
Dec 19, 1993, 3:37:22 PM12/19/93
to
Robb says that you could envisage the C compiler parsing the format
string in printf calls.

No you couldn't, it is perfectly permissible to compute this string
at runtime. True enough it is often a literal, but it is not required
to be!

Alex Blakemore

unread,
Dec 19, 1993, 9:49:34 PM12/19/93
to
Robert Dewar writes

> Robb says that you could envisage the C compiler parsing the
> format string in printf calls. No you couldn't, it is perfectly
> permissible to compute this string at runtime.

true enough, but the GNU compiler will check the format strings
at compile time if you use the -Wall option, a nice feature.
for example:

#include <stdio.h>
void main(void) {
int i = 1;
printf ("this %s is a bug %d\n", i);
}

produces:
x.c:4: warning: format argument is not a pointer (arg 2)
x.c:4: warning: too few arguments for format
--
Alex Blakemore
al...@cs.umd.edu NeXT mail accepted

Keith Thompson @pulsar

unread,
Dec 24, 1993, 5:04:14 AM12/24/93
to
In article <2ektqn$i...@corona.VisiCom.COM> t...@VisiCom.COM (Tom Quiggle) writes:
...

> For another data point, I ran the above code through the only two Ada
> compilers I have available on this machine: the old TeleSoft
> RISCAda/SPARC compiler (RIP), and the Verdix VADSworks compiler for the
> 68020.

Presumably Tom's "(RIP)" refers to TeleSoft (absorbed by Alsys) and not
to the RISCAda/SPARC compiler, which is still a product developed and
sold by Alsys.

(Disclaimer: If I spoke officially for Alsys, I suspect I'd have said
something fairly similar, but I don't.)

--
Keith Thompson (The_Other_Keith) k...@alsys.com (k...@telesoft.com still works)
TeleSoft^H^H^H^H^H^H^H^H Alsys, Inc.
10251 Vista Sorrento Parkway, Suite 300, San Diego, CA, USA, 92121-2718
"Hush my darling, be still my darling, the lion's on the phone" -- TMBG

0 new messages