4 ms·
C is not assembler nor is it close to assembler nor is it desirable to have a C compiler in your toolchain to compile something that needs assembler For exampl
by duped 3y ago
C is not assembler nor is it close to assembler nor is it desirable to have a C compiler in your toolchain to compile something that needs assembler
For example, C has a calling convention. It has a notion of a stack and heap. Certain things like arbitrary control flow, threading, and process spawning cannot be expressed in C. It has a memory model that you must adhere to.
If you actually want to implement a programming language, C is not sufficient as a transpilation target except for mostly trivial designs.
- kragen 3y agoworks pretty well most of the time; chicken scheme even compiles call/cc to c
- lelanthran 3y ago> For example, C has a calling convention. It has a notion of a stack and heap. Certain things like arbitrary control flow, threading, and process spawning cannot be expressed in C. It has a memory model that you must adhere to. Yes, all trivially bypassed or worked-around if you're emitting C. Considering the initial constraint was "cross-platform": 1. Calling convention - what would be the point of a calling convention different to the rest of the system? You wouldn't be able to do system calls, for one. And if you need a different calling convention within the language itself C will let you push elements onto a stack and jump to a different piece of code[1], optionally saving and restoring registers. 2. The notion of a stack and heap - what's the alternative you have in mind when using assembler? Assemblers also have a notion of a stack and non-stack addresses, after all. 3. Arbitrary control flow - you can emit goto's to your hearts content (see [1] below). 4. Threading - this is platform-specific by nature, but if you're emitting C you can emit calls to the platform's thread primitives (CreateThreadEx, pthread_create, etc) the same way you'd have to do if you were using an assembler. Nothing stops you from proving a wrapper for threads that your emitted C code calls (so that it works on multiple platforms). 5. Process-spawning - once again, this is platform-specific, so even in assembler you're going to have to make a call to the platform's CreateProcess/posix_spawn/exec functions. If you're going to call a platform function from assembly, you may as well call it from C. 6. Memory model - I'll give you this point, even though the memory model is not that much different from assembler. If you're emitting C, you're still free to allocate/define a large array and use that memory in whatever way you wish. > If you actually want to implement a programming language, C is not sufficient as a transpilation target except for mostly trivial designs. Actually, many languages with non-trivial and advanced features, such as Common Lisp, already have implementations that transpile to C. They implement the entire Common Lisp standard, and don't seem to be missing any features even though they are emitting C and not assembler. Anonymous functions? Done with emitted C. Closures, continuations? Done with emitted C. Garbage collection? Same. What feature do you have in mind that cannot be implemented by emitting C[2], but can be implemented by emitting assembler[3]? [1] Some constraints on the jumping exist, but emitting GCC-specific C let's you do calculated expressions for the jump target - you don't need to `goto` a constant address. [2] I initially thought that something like Goroutines or async functions might be a candidate, but off the top of my head, I can think of at least one way to implement async functions and/or go routines by emitting C. There's probably more. [3] I'm not being contentious, I'd really rather like to know - my own toy language (everyone has one) currently in the phase of "I'm thinking really hard about what it should look like" is going to be emitting C. I couldn't find any feature for the language that can't be done by emitting C, so I really want to know what you have in mind.
- vidarh 3y agoPlenty of languages have language-internal calling conventions nothing like C, and usually for good reasons. Some try to stay close to make passing the boundary easy, some are so different they need an expensive conversion step anyway and then there's little point. Sure, you can always map your requirement onto the standard calling convention somehow, but if you can't directly call out or in anyway it's a constraint it's totally reasonable to not want to be forced into. EDIT: Note that there's nothing wrong with emitting C if you don't have any compelling reasons not to. When we don't it's often for reasons such as wanting a simpler toolchain, wanting the environment to be self-hosted etc. that are tradeoffs that sometimes are frankly ideological (take that as you want - I don't mean anything inherently negative with that other than when the authors ideology differs from mine ;) I like that there's choice and that some deviates from the norms ), sometimes based on requirements that doesn't really have to do with the language itself. I do agree with you that you can fit all languages onto C, and most languages neatly onto C. One example where it has traditionally been painful without compiler extensions, though, is tail call elimination. You can still do it. Other fun times involve closures, but you can do that too (been there, done that, wrote a blog post years ago) even though it quickly gets ugly. Often you end up wanting to demand a specific set of extensions. Overall nothing stops you, but at some point it also doesn't necessarily buy you all that much - you still need to do most of the hard bits.
- lelanthran 3y ago> Sure, you can always map your requirement onto the standard calling convention somehow, but if you can't directly call out or in anyway it's a constraint it's totally reasonable to not want to be forced into. I get that, but what I don't get is what about C makes it harder to define your own calling convention than defining it in an assembler language. In the context of this thread, OP wants a portable assembler. Implementing a custom calling convention by emitting any assembly language is bound to be more work than implementing a custom calling convention by emitting C. Maybe the situation regarding floating point registers when passing floating point values? That's about the only thing I can think of that makes it harder to emit C code that implements a custom calling convention than doing it in assembler, but even for that I can think of workarounds, just so that other high-level constructs can be used.