Live data from Hacker News

First C compiler ported to GCC

github.com

71–80 of 90 posts

Re: First C compiler ported to GCC

#71
post #19

If the first C compiler was written in C... how could it be first C compiler? How could you compile the first C compiler?

https://bootstrappable.org/

Probably out of topic, but are there real examples of compiler attacks due to bootstrapping ? I did not hear about them before reading the scifi classic Accelerando by C. Stross

Re: First C compiler ported to GCC

#72
post #24

Earlier quoted context omitted.

I think he means that int and pointer address must be interchangeable. As long as that holds, the size can be either 16 bits or 32 bits. On a PDP-11 int would have been 16-bit. On x86 32 bits. But on x86_64 int is 32 bits but pointers are 64-bit. The easiest way to retain the original assumption with minimal changes to the historical source code while targeting a modern CPU is to compile in 32-bit mode.

Why can't it be 64-bit? I don't see any reason why we can't have an ILP64 data model. If int and int* were both 64-bit then it would restore so much of the original beauty of C.

It can be, but people have arranged for it to not be, presumably because they don't feel the storage space to have all integers be 8-bytes is not justified.

Re: First C compiler ported to GCC

#73
post #9

Earlier quoted context omitted.

One of the unusual things in this early version of C is that "int" can be used for any word-sized value, including pointers. The type system was very loose.

Even back then this was considered poor practice, however. The first edition of K&R had a subsection entitled "Pointers are Not Integers" (I don't know if that's still in modern editions).

The interesting thing for me is that a variable without a type annotation could potentially store anything. It kind of explains why the language used "int" as the default type of variables declared without a type annotation.

Re: First C compiler ported to GCC

#74
post #24

Earlier quoted context omitted.

I think he means that int and pointer address must be interchangeable. As long as that holds, the size can be either 16 bits or 32 bits. On a PDP-11 int would have been 16-bit. On x86 32 bits. But on x86_64 int is 32 bits but pointers are 64-bit. The easiest way to retain the original assumption with minimal changes to the historical source code while targeting a modern CPU is to compile in 32-bit mode.

Why can't it be 64-bit? I don't see any reason why we can't have an ILP64 data model. If int and int* were both 64-bit then it would restore so much of the original beauty of C.

It can be, and is, on platforms where supporting large arrays (if integers are 32 bits, arrays can ‘only’ have 2³¹ entries (#)) is deemed more important than memory usage.

(#) https://software.intel.com/content/www/us/en/develop/documen... seems to imply that limit is 2³¹-1. I don’t understand why that would be true.

Re: First C compiler ported to GCC

#75

Earlier quoted context omitted.

Not everything needs a valid use case. It can exist just for fun.

The thing is, the more complex a spec is (or rather, how much stuff it allows that will never be used), the bigger the danger is that somewhen down the line, this will introduce a security or other issue.

It's not an array trick, it's a definition of arrays.

Re: First C compiler ported to GCC

#76
post #59
post #29

Earlier quoted context omitted.

The first B compiler was actually written in TMG, and once it was bootstrapped that way in B itself. BCPL was only the inspiration for the language.

Wow, TMG was a new one for me. From the Wiki article on it: "Douglas McIlroy ported TMG to an early version of Unix. According to Ken Thompson, McIlroy wrote TMG in TMG on a piece of paper and "decided to give his piece of paper his piece of paper," hand-compiling assembly language that he entered and assembled on Thompson's Unix system running on PDP-7." We are not worthy, friends. We are not worthy.

I did pretty much the same thing with https://github.com/kragen/peg-bootstrap/blob/master/peg.md, although admittedly hand-compiling to JS was noticeably less work than hand-compiling to assembly language would have taken. My friend Dave did something similar with Val Schorre's META-II: https://queue.acm.org/detail.cfm?id=2724586 (missing the figures for some reason, so see:) doi:10.1145/2697401

Re: First C compiler ported to GCC

#77

Earlier quoted context omitted.

And that's how it's still taught nowadays. Both the C89 and the C99 standard draft contain the following: > The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))) In fact the expressions a[b] *(a + b) and b[a] are equivalent. Here is a perfectly valid snippet of C code that will print out 't': putchar(3["test"]);

How does this work in C++ with operator overloading. Are they still the same? That would make for some interesting obfuscated code.

No, they're not. It works by: if either a or b in a[b] is a class/enumeration type, call a.operator[](b).

Re: First C compiler ported to GCC

#78
post #9

Earlier quoted context omitted.

One of the unusual things in this early version of C is that "int" can be used for any word-sized value, including pointers. The type system was very loose.

Even back then this was considered poor practice, however. The first edition of K&R had a subsection entitled "Pointers are Not Integers" (I don't know if that's still in modern editions).

It looks to me like that section was removed in the 2nd edition. Some sections moved around, so maybe I'm just looking in the wrong place, but it's not nestled between "5.5 Character Pointers and Functions" and "5.7 Multi-Dimensional Arrays" like it is in the 1st edition.

Re: First C compiler ported to GCC

#79

Earlier quoted context omitted.

And that's how it's still taught nowadays. Both the C89 and the C99 standard draft contain the following: > The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))) In fact the expressions a[b] *(a + b) and b[a] are equivalent. Here is a perfectly valid snippet of C code that will print out 't': putchar(3["test"]);

I understand why that example works but I struggle finding a valid use-case, aside from code golfing...

It isn't a use case; it is a drawback of the C array and pointer semantics:

- Array values decay to pointers in rvalue contexts (though not as the argument of sizeof);

- a[b] is syntactic sugar for *(a+b).

— ⁂ —

These two design decisions have some desirable results:

- Arrays, including strings, can be in effect passed as arguments to functions without implementing a special parameter-passing mechanism for arrays.

- Functions on arrays are implicitly generic over the array length, rather than that length being a part of their type. (When this isn't what you want you should probably be using a struct instead.)

- Array iteration state can be represented as a pointer, preventing bugs in which you index the wrong array. In a sense a single pointer represents an array range or slice, as long as you have some way to identify the array end, like nul-termination in strings or a separate length argument.

- You can change a variable (including a struct field) from being an embedded array to being a pointer to an array allocated elsewhere—or vice versa—without changing the code that uses it. (But if this had been a significant design consideration, -> wouldn't be a separate operator from . in C.)

- It's easy to create new "arrays" at runtime: just return a pointer to some memory.

— ⁂ —

Like all design tradeoffs, these also have some drawbacks, which are so severe that no language of the current millennium has followed C's lead on this, although many of C's other design decisions are wildly popular:

- Bounds checking is impossible.

- Alias analysis for optimization is infeasible.

- If you aren't using a sentinel, you have to pass in a separate argument containing the array length whenever you pass in an array pointer, or stuff these base and limit fields into a slice struct, or something.

- Arguably, these decisions are hard to separate from the fact that C strings are terminated by a sentinel value and thus are not binary-safe.

- 3["hello"] is legal C.

— ⁂ —

Of these five drawbacks, the fifth seems like it may not be as severe as the other four?

Re: First C compiler ported to GCC

#80
post #19

Earlier quoted context omitted.

https://bootstrappable.org/

Probably out of topic, but are there real examples of compiler attacks due to bootstrapping ? I did not hear about them before reading the scifi classic Accelerando by C. Stross

Oh, the thing with bootstrapping attacks is you never know are there any real examples.

I recommend this[0] paper by Ken Thompson dated 1984 and still relevant.

[0]: https://www.cs.cmu.edu/~rdriley/487/papers/Thompson_1984_Ref...

Post reply on HN