8cc: A Small C Compiler
github.com
github.com
> No memory management is a memory management scheme in 8cc. Memory regions allocated using malloc are never freed until the process terminates. That has greatly simplified the code and the APIs because 1) you can write code as if garbage collector were present, and 2) that design decision has eliminated use-after-free bugs entirely.
>DMD does memory allocation in a bit of a sneaky way. Since compilers are short-lived programs, and speed is of the essence, DMD just mallocs away, and never frees. This eliminates the scaffolding and complexity of figuring out who owns the memory and when it should be released. (It has the downside of consuming all the resources of your machine if the module being compiled is big enough.)
nwcc is a bit more mature. It supports multiple architectures, multiple OSes, and it can bootstrap GCC.
I have tested on both Ubuntu and arch linux.
Fabrice Bellards brain is incredible at getting functionality in a small amounts of code, but the code is not great for teaching.
It was packaged as kencc for *nix, in honour of Ken
0c spim little-endian MIPS 3000 family
1c 68000 Motorola MC68000
2c 68020 Motorola MC68020
4c mips64 little-endian MIPS64
5c arm little-endian ARM
6c 960 Intel i960 (historical)
6c amd64 AMD64 and compatibles (e.g., Intel EM64T)
7c arm64 little-endian ARM64
7c alpha Digital Alpha APX (historical)
8c 386 Intel i386, i486, Pentium, etc.
9c 2900 AMD 29000 (historical)
9c power64 64-bit POWER
kc sparc Sun SPARC
qc power Power PC
uc sparc64 SPARC64
vc mips big-endian MIPS 3000 family
xc 3210 AT&T DSP 3210 (historical)
zc avr Atmel AVR
Some are more mnemonic than others.And 8cc shares the similar KISS principles as 8c, by far simpler than the big and slow c compilers gcc, clang, CompCert.
That said, I get a feeling this one has many more files than necessary, as a lot of them only have a few functions in them that could be consolidated into one "utilities" file.
It may be good for reading that one file, but not for understanding the whole.
This is common in many languages. Java enforces one (public) class per file, which is also a common coding style in C++, Python, Ruby and PHP, even though those languages don't enforce that.
You also find this in more modern languages like OCaml, where it is recommended to have one "module" per file, and one "module" concentrates on one data type. (and maybe one or two helper types if needed, much like in-class type definitions in C++)
This may be unusual in C projects, but it does have its merits. I read the code and wonder "Oh, what exactly is that 'dict' stuff?". Then, I can blindly guess the file names "dict.h" and "dict.h". I see a full explaination of "dict" and nothing else. I then notice that it is a combination of "map" and "vector", and maybe that's all I need to know. If not, I dig into "map.c" and "vector.c".
Otherwise, I'd have to search, and finally find it in some unrelated file name like "utils", "utililities" or "mapvectorutils". Then I have to search for all dict-relevant code in that file, skipping through a bunch of unrelated code.
To get the "whole picture", I'd prefer to see a simple diagram (autogenerated by IDE, or as part of the docs) that shows which module depends on which other module, maybe somehow arranged into layers. The other strategy "group multiple modules into a file" may convey the highest level quite well, but utterly fails to explain the dependencies of the modules inside a single file.
I'm a fan of bigger and fewer files. Atomized bits of functionality are tedious to read, when you have to read lots of code to understand the gestalt.
Indeed, this is mostly a matter of taste. Both styles have their merits and downsides. For some projects, I also preferred bigger files up to single-file solutions, which also worked great.
> In an IDE, "go to definition" solves this problem. In an editor, incremental text search over the entire project solves this problem
When using the right tools, it doesn't matter how the code distributed across files. But I believe this is slightly out of scope for the question at hand.
Ad Hominem should have to place at HN. Please don't do that. https://news.ycombinator.com/newsguidelines.html
That's my professional opinion.
Nobody said that.
Please read more carefully, especially before goofing off others. Or, simply don't goof off anyone in the first place.
Needing to look into a file called "utils.c" or "datastructures.c" is less clear than what it is currently imo, so I support your design.
"Modern" languages like Java/C# tend to scatter code over many small files. I think it's a cultural thing: since IDEs do not have "split" mode that provides several views to different parts of the same file side-by-side (like Vim does), programmers tend to split code into files small enough to scroll fast and IDE handles switching between files. Plus the dogma "one class, one file" that limits file size even if it's not helpful.
I'm glad that functional programming is on the rise: it divides code units more naturally with modules of functionality, not a one-size-fits-all programming language structure like classes.
P.S. Also, composition of a big source code file conveys some "background" information you'd have to recover from a needlessly fragmented pile of small files.
I'm not sure this will change anything. For instance, OCaml's modules are usually a bunch of functions surrounding a single abstract type. This is very similar to classes in other languages.
Lets not forget what comes first. C++11 adoption was held back a few years because MSVC didn't have support for those features.
Or worse, as if most IT departments are allowing the use of C++11 compilers in corporate environments.
Perhaps someone could even make the argument that a programming language is BEST designed by someone who doesn't know how to make a compiler?
The choices for a programming language should purely be based on the language, and on the user experience. In the design phase no thought will be given to compiler design (initially) and all of that will eventually be hiddden as implementaiton details.
Performance is also extremely important for language ux, and there are certainly constructs that will prevent your language from ever being efficient. Someone completely unfamiliar with compiler design could easily add these to the language on features that are simply not worth it, even in his or her own eyes.
Besides, have you ever looked back on something and said 'Gee, I wish I knew less about this when I started'
I'd note, though, that long compile times shouldn't be too dependant on language design, should it?
I feel that an argument could be made that if a programming language is well defined, then a well written compiler could be well thought out and designed around it.
Of course, I'm sure that there are counterpoints. I'm sure that there are languages for which it is very difficult to write a "fast" compiled.
> Besides, have you ever looked back on something and said 'Gee, I wish I knew less about this when I started'
Actually, I kind of have. Experts (for lack of a better term) sometimes make their solutions more complicated than they need to be, and this is partly because they have the knowledge to do that.
I think that's caused by a slightly different effect - not considering essential and incidental complexity. It is true that for some programmers, the urge to put in as many things as they know tends to override the more basic goal of solving the problem in the most direct way.
I think your point is that too much knowledge may hinder your view. That's certainly something you should watch; but in general, I think risk of making bad decision from ignorance is greater.
This was the point of "Worse is Better" article. http://www.jwz.org/doc/worse-is-better.html
But many of the worst offenders in such languages also have little to do with "nice features" and more to do with not thinking about compilation. E.g. one of the big issues with compiling Ruby is that classes can generally be re-opened at any time.
It's an awesome feature for a lot of things. But it also makes it incredibly hard to formally reason about Ruby code.
What does "1+1" mean? You can't know without knowing whether or not someone has overridden Fixnum#+. And that can happen anywhere. So even if you know that 1+1 is 2 now, after the next method call, you don't know any more unless you've determined exactly what the intervening method call was, and traced everything it might have called. Oh, and you need to take into account threading. Executing 1+1 in a tight loop, expecting it to mean the same on every iteration? Can't safely do that, as someone might be randomly redefining methods in another thread. Even if you determine that that can't happen now, if anyone ever calls eval() on data you can't statically determine (or its cousins such as "require"), you're back to square one.
Of course 99% of the time nobody has a sane reason to do that, and the 1% (or more like 0.001%) when it happens, it's almost always because someone is proving a point, and nobody would care about breaking the proving-a-point cases. Outlawing overriding methods of the basic system classes, or even "just" specifying a subset of basic methods that the implementations should treat as immutable would make optimising Ruby code immensely much easier with very little practical impact. (Ruby has the woefully underused "freeze" but that applies only to objects; a method-level "freeze" or "final" carefully applied to a few dozen rarely-if-ever methods would make a lot of difference; it'd even be fine to supply a way of overriding it, as at least then you'd be signalling clearly that you're about to do something that requires disabling certain optimisations).
Providing restricted versions of "eval" (so you can provide guarantees about what will still be the same after eval), and the same for "require" would also make a huge difference (e.g. if I had a way of loading new code and being sure that it only defined new classes rather than modifying existing classes, I would be able to aggressively inline a lot of things in situations where I could determine types statically; without it, I can still do inlining but needs to do a lot of work to verify when changes have occurred and potentially fall back on a much slower path, which seriously increase the complexity and reduces the benefit of inlining)
The strength of language designs coming from people who are experienced at writing compilers is that you rarely see those kind of situations, because the first thing a compiler writer would tend to want to do would be to declare a lot of corner cases like the above as "undefined behaviour" at a minimum, and proceed to implement optimisations that would utterly break it.
While Ruby is my favourite language, my favourite influence when it comes to writing compilers is Niklaus Wirth, who from his first language work on successors to ALGOL-60 has consistently been working to reduce the complexity of his languages with each iteration (he has usually removed more than he has added in each new language). One might very well believe Wirth is too much of a purist, but while his languages tended towards the minimal, his overall principle is worth considering: He only wanted to retain functionality in his languages with proven benefit and that he understood how to implement efficiently. A lot of languages could benefit from extensive pruning of functionality on that basis.
(Incidentally, for my Ruby compiler if/when I finally get to the stage when these things matters, I do intend to deviate from Ruby and declare a whole lot of things that people don't generally ever do but that should technically work as "undefined behaviour unless you specify the --generate-horribly-slow-but-compatible-code switch or something to that effect)
Twitter had to change to java, reddit had to change from lisp to python(and still has performance issues constantly), facebook had to write a php compiler, minecraft had to write a C++ version... There's no free lunch.
The intersection of people who find a particular niche problem and also knows how to do parsing, compiling and optimizing well is pretty small.
Anecdotally, I've found the compiler people universe and the web developer universe to be pretty disjoint for a long time.
There is no reasonable way of improvement. A problem that is solved in Django 1.8 by making it possible to easily switch template engines.
Now it just need to parse the FreeBSD stdio.h correctly..