6 ms·
It's true and false at the same time. The operations C gives you map 1-to-1 with assembly. Given some C code you can quite accurately predict which loads/stores
by PhilipRoman 3y ago
It's true and false at the same time. The operations C gives you map 1-to-1 with assembly. Given some C code you can quite accurately predict which loads/stores will be elided by the compiler and what the resulting assembly will be.
I can't name another language for which this is true.
I get that you're hinting at the insane level of undefined behaviour enforcement by compilers, but I don't think it matters all that much once you understand how optimizing compilers work.
- simias 3y ago> It's true and false at the same time. The operations C gives you map 1-to-1 with assembly. Given some C code you can quite accurately predict which loads/stores will be elided by the compiler and what the resulting assembly will be. I mean, can you though? After all you won't even have the same output depending on compiler and flags. And of course not all architectures have the same capabilities, so the same code can compile to a various number of instructions depending on the target architecture. Not to mention things like bitfield access that can result in non-atomic load/stores for a simple `foo->bar = 1;` I'm not sure in what sense you could say that C operations map 1-to-1 with assembly any more than Rust, C++ or basically any compiled language.
- Joker_vD 3y ago> The operations C gives you map 1-to-1 with assembly int test(int x, int y) { return x % y; } test: // @test sdiv w8, w0, w1 msub w0, w8, w1, w0 ret Surprisingly, assembly doesn't have the remainder instruction, but instead it has a multiply-then-subtract instruction which is not corresponding 1-to-1 to anything in C.
- IshKebab 3y agox86 doesn't have remainder. Other instruction sets do. But yeah I agree with your point. Some targets might not even have multiply.
- Joker_vD 3y agox86 has remainder: IDIV calculates both the quotient and the remainder at the same time and places them in different registers just like many other ISAs before it did. In fact, DIV instruction of PDP-11 worked that way too: Description: The 32-bit two's complement integer in R and Rv1 is divided by the source operand. The quotient is left in R; the remain- der in Rv1. Division will be performed so that the remainder is of the same sign as the dividend. R must be even. Example: CLR RO MOV #20001,R1 DIV #2, RO Before After (RO) = 000000 (RO) = 010000 Quotient (R1) = 020001 (R1) = 000001 Remainder PDP-11 also had ADC and SBC (add/subtract with carry) instructions which weren't (and still aren't) exposed in C either.
- messe 3y ago> x86 doesn't have remainder The code in the comment you replied to is 64-bit ARM assembly.
- IshKebab 3y agoOops of course.
- halayli 3y ago> The operations C gives you map 1-to-1 It does not. Implicit casting, inlining, volatile, args passed via registers vs stack etc can significantly change what you expect to be generated.
- jstimpfle 3y agoPretty much a matter of optimization isn't it? Try disabling them. But I take it that this was never the point anyway. I think the point is that the representation of language objects and the runtime are rather straightforward compared to many other languages.
- chc 3y agoOnly one of those (inlining) is an optimization. Two are language features (implicit casts and volatile) and the other is a calling convention (passing arguments on registers vs. stack).
- flohofwoe 3y agoCalling conventions aren't (mainly) a C feature either though, but are defined by the ABI specified for a specific OS/CPU combination (and all languages which want to talk to system APIs need to implement that ABI, not just C).
- pornel 3y agoAnd conversely you don’t have access to a few things assembly can do: arbitrary stack access, PC register, flags. Some operations like bit rotation, zero bit counting, or fancier addressing modes have informal code patterns with a hope they’ll optimize right, but nothing guaranteed in the standard.
- xigoi 3y agoIn C, unlike pretty much every other language, you can't even know how big an integer is.
- coliveira 3y agoOf course you can, but this is implementation defined. So you need to create code that does different things based on the underlying implementation. Every reasonably large C code base checks the current implementation to define what type of integers, pointers, etc. they're dealing with. This is by design, because C was created do deal with disparate processors and OSs. When it was crated it would be difficult and unwise to assume that an integer has size of 2 bytes, for example, since each machine would define its own preferred length.
- IshKebab 3y ago> When it was crated it would be difficult and unwise to assume that an integer has size of 2 bytes It was still a bad design. Of course this is in hindsight, but history has taught is it would have been much better if the default was for specific sized integers, with an option to use `uint_fast32_t` or whatever.
- gpderetta 3y agoThen we would be stuck with 16 bits integers. We are lucky to be in a point in time where integer types have been stable for a while, but it wasn't always so.
- IshKebab 3y agoWe wouldn't. People would update their code eventually or opt in to variable sized integers.
- coliveira 3y agoHistory says otherwise: C was so successful it is still widely used.