8 ms·
Not really, assembly language is extremely close to microcode, much much closer than it is to C. C has a whole bunch of baggage like pointer provenance, inabili
by dooglius 7y ago
Not really, assembly language is extremely close to microcode, much much closer than it is to C. C has a whole bunch of baggage like pointer provenance, inability to read uninitialized memory, doesn't understand cache aliasing, etc that don't fit hardware well.
- radarsat1 7y ago> pointer provenance, inability to read uninitialized memory what do you mean by these two? not sure about the former. as for the latter, of course you can read uninitialized memory, unless you mean something else?
- umanwizard 7y agoNo, reading uninitialized memory in C is UB.
- tspiteri 7y agoAs far as I know (and I could be wrong), reading uninitialized memory in C is not UB, but gives an indeterminate value, which may be either an unspecified value or a trap representation. If it is a trap representation, accessing it is indeed UB, but if all possible values of your memory are valid, e.g. if you have an unpadded integer where every bit representation means something valid, then it is not UB, though it is still an unspecified value which does not even have to be consistent: you could get different values when reading the same uninitialized location twice. Edit: Some excerpts from the C standard: 6.7.8 Initialization 10 If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate. 3.17.2 indeterminate value either an unspecified value or a trap representation 3.17.3 unspecified value valid value of the relevant type where this International Standard imposes no requirements on which value is chosen in any instance 6.2.6 Representation of types 6.2.6.1 General 5 Certain object representations need not represent a value of the object type. … Such a representation is called a trap representation.
- lmkg 7y agoNot a C expert, just reading what I can find online: This page says value is indeterminate, which is either unspecified or a trap, as you say: https://wiki.sei.cmu.edu/confluence/display/c/EXP33-C.+Do+not+read+uninitialized+memory https://wiki.sei.cmu.edu/confluence/display/c/EXP33-C.+Do+no... But. This part says that reading an indeterminate value is, in fact, undefined behavior (line 11 in the table): https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_11 https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+...
- tspiteri 7y agoThat says used, not read. And indeterminate, not unspecified. My point was on reading a value, not on acting on the value. I know it looks like nit-picking, and you should just not read uninitialized memory, but I don't think it's consistent with the standard to say that reading uninitialized memory is always UB.
- lmm 7y agoWhat kind of read would not constitute "using" a value? And per your previous post an object that was not initialised is indeterminate, not just unspecified. So yes, reading uninitialized memory is always UB.
- deleted 7y ago[deleted]
- deleted 7y ago[deleted]
- madmax96 7y agoA read into a character type. From 6.2.6.1¶5: >Certain object representations need not represent a value of the object type. If the stored value of an object has such a representation and is read by an lvalue expression that does not have character type, the behavior is undefined. ... Such a representation is called a trap representation. A read from uninitialized memory is not always UB.
- eternalban 7y agoNo. It is not "undefined behavior". The "behavior" here is assignment (reading memory). It always will behave precisely and consistently: it _will_ assign the "indeterminite" value of the un-initialized memory. Using such an "indeterminite value" in other operations (e.g comparison per the link Steve B. posted above: https://www.ralfj.de/blog/2019/07/14/uninit.html https://www.ralfj.de/blog/2019/07/14/uninit.html) is the UB bit.
- throwaway_pdp09 7y ago> [reading memory} always will behave precisely and consistently What is the point of not making it UB? You can't AFAICS possibly rely on any useful way on it behaving 'precisely and consistently' so just make it UB anyway?
- saagarjha 7y agoI think this lets you copy it around, e.g. when you have a struct that you have partially initialized.
- throwaway_pdp09 7y agoThat makes sense, finally. Thanks.
- chrisseaton 7y agoCan you write a well-defined C program that reads uninitialised memory?
- dooglius 7y agoNot portably, you could of course use inline assembly in practice, but the normal way of doing it is UB.
- BenjiWiebe 7y agoWouldn't this do it? int a; printf("%d", (&a + sizeof(int)));
- chrisseaton 7y ago...but that isn't a well-defined program, is it?
- saagarjha 7y agoReads? I think so; I believe the bad part is when you try to use it.
- chrisseaton 7y agoNo, it is undefined behaviour. Check the spec.
- saagarjha 7y agoI couldn't find the verbiage for that. The standard seems to have this under undefined behavior: > The value of an object with automatic storage duration is used while it is indeterminate. But reading the value without using it seems fine?
- chrisseaton 7y agoThe 'object' in this context is the storage location, not the value you read from it. You're 'using' the 'object' when you read from it.
- steveklabnik 7y agoFor the first, https://blog.regehr.org/archives/1621 https://blog.regehr.org/archives/1621 For the second, https://www.ralfj.de/blog/2019/07/14/uninit.html https://www.ralfj.de/blog/2019/07/14/uninit.html (the second one is a bit more Rust focused but the core idea is the same)
- deleted 7y ago[deleted]
- umanwizard 7y agoAnother major way in which assembly language is closer to the hardware than C is the presence of explicit SIMD instructions.
- vardump 7y agoNo, it really isn't on anything modern, like ARM SoCs or x86 CPUs. The number of times I've hit my head to wall trying to understand what the heck the CPU is doing for a given sequence of instructions, like why the number of cycles or memory bandwidth required is way different from my expectation. A modern CPU willy nilly reorders instructions, stores, and creates new "virtual" registers out of thin air to dissolve stall inducing dependency chains. It can also split instructions, fuse them together and even entirely remove some instructions -- as long as it doesn't affect the end result. Generally (within limits of CPU memory model) the only thing you're guaranteed is that eventually the result is what you'd expect from sequential execution. That of course applies on the core you're running on, otherwise you naturally need to synchronize with other cores. Luckily there are tools to find out, which work to some extent, IF you can run it on same CPU model (and really, whole system, incl. memory subsystem!) as what you're interested in.
- dooglius 7y agoCan you give an example? The most complicated assembly->microcode examples I can think of are floating-point instructions like FSIN that run CORDIC loops under the hood, and those are still pretty easy to reason about.
- vardump 7y agoPseudo-code: a = 10; b = 20; c = b + 30; bar = a + b; x = 1; y = 2; z = y + 3; foo = x + y; ... in order to remove dependency stalls might be executed as: b = 20; y = 2; a = 10; x = 1; c = b + 30; z = y + 3; bar = a + b; foo = x + y; This would still be true, even if 'x' and 'a', 'y' and 'b' etc. variables (well, registers) had same name. In that case CPU would just make up new "variables" as required and do the same transformation!
- dooglius 7y agoOkay, you're talking about OoO execution here, and you're right that this can be hard to reason about (in terms of stalls/latencies), however that's orthogonal to microcode translation.
- gruez 7y ago>inability to read uninitialized memory printf("%x", *(int*)0x12345678);
- deleted 7y ago[deleted]
- chrisseaton 7y agoThis is not a well-defined C program though.
- fennecfoxen 7y agoAt some point, the question of "well-defined C program" is philosophical. Let's get down to brass tacks: If someone sat down and wrote this, would it happily compile? Would it compile under some compilers and configurations but not others? Would it throw warnings instead of errors under some configurations? Which of these configurations are the default configurations? Which non-default configurations are considered best practices? How complex are the best practices, and how widespread is knowledge about them? In short, how much does the concept of a "well-defined C program" differ from the concept of a "C program" as implemented in practice? Can we say that most C programs, or even a medium-sized percentage of C programs, are well-defined?
- deleted 7y ago[deleted]
- overgard 7y agoMost C programs are indeed not well-defined. Probably most of them.
- hexane360 7y agoEven casting 0x12345678 to int* is undefined behavior
- icedchai 7y ago
- overgard 7y agoI'm intrigued that you make that claim when as far as I know most microcode is proprietary and not necessarily documented. Also that claim is highly dependent on which cpu and manufacturer you're talking about, if the cpu needed to be updated, etc.
- dooglius 7y agoThe micro-architectures are reasonably documented enough (e.g. we know what each the execution ports do, that register renaming is a thing) and if you look at e.g. Agner Fog's instruction tables [0] that map macro-instructions onto latencies and port counts, that gives a pretty reasonable picture of what's going on. [0] https://www.agner.org/optimize/instruction_tables.pdf https://www.agner.org/optimize/instruction_tables.pdf
- compiler-guy 7y agoIn other words, the assembly code itself isn't really a good picture of what is happening.
- overgard 7y agoLet me up the ante a bit. You also have to consider out-of-order execution, and instructions being executed in parallel, and speculative execution. Let me quote wikipedia here: > The Pentium 4 can have 126 micro-operations in flight at the same time. Micro-operations are decoded and stored in an Execution Trace Cache with 12,000 entries, to avoid repeated decoding of the same x86 instructions. Groups of six micro-operations are packed into a trace line. Complex instructions, such as exception handling, result in jumping to the microcode ROM. During development of the Pentium 4, microcode accounted for 14% of processor bugs versus 30% of processor bugs during development of the Pentium Pro. Like, that's one paragraph, there are others I can cherry pick to show just how complicated the execution of microcode is. Unless you're an actual engineer on processor development and have insider info, I find it highly suspect that you could look at a decently sized chunk of assembly code and really know what happens with the microcode. Sure you can have a table that says, it takes ballpark about this long. Ok fine, but that's not the same thing as assembly mapping cleanly to what the processor is actually doing.
- Jasper_ 7y agoMicrocode is not used for the majority of operations. Are you confusing it with micro-ops?
- dooglius 7y agoMaybe you have a different definition in mind than I do? I'm using microcode to mean sequences of micro-ops. Wikipedia [0] seems to agree, "the microcode is a layer of hardware-level instructions that implement higher-level machine code instructions or internal state machine sequencing in many digital processing elements". My understanding is that with a couple exceptions (IIRC mov and zeroing xor are treated specially as part of register renaming) all assembly instructions get translated into microcode (i.e. a sequence of micro-ops). [0] https://en.wikipedia.org/wiki/Microcode https://en.wikipedia.org/wiki/Microcode
- Jasper_ 7y agoI don't have the patience to go fix Wikipedia, but microcode is a patching system (it's what "processor microcode updates" means). Most of the time, that's adjusting chicken bits and other flags. Instructions can be implemented in microcode, but they are really, really slow so it's typically done for security reasons or to emulate some new features that don't require fast performance. Micro-ops are part of the micro-architecture of the processor, and are in hardware. They are not patchable and are not software.
- vardump 7y agoMicrocode on x86? Don't forget to also greet our friends such as RDTSCP, CPUID, RDMSR, POPCNT (on some models) etc. Also remember to check ENTER, BOUND, etc. out in the museum vitrine. But yeah, microcoded instructions are relatively rarely executed.
- Jasper_ 7y agoI can't find any evidence that RDTSCP is microcoded. That would defeat the whole purpose of a high-performance counter. Any source?