The funny thing about the PDP-11 instruction set is how ridiculously simple it is to write a C compiler for it… the C operands map almost 1 to 1 to PDP-11 instructions.
At least that is what my compilers writing class taught me 25 years ago ;-)
As a PDP-11 assembly programmer who migrated to C, this one has caught me out too because it does 'feel' a very natural explanation. Possibly it's because both products came from the ideas that were in circulation at the time - or maybe it's that PDP-11 assembly programmers who moved to C found it easy (given pointers with pre/post-increment/decrement addressing is baked into the instruction set).
I'm not really seeing it, at least not in this opcode list.
As far as I can tell the C operands are fairly simple and basic (add, subtract, complement, and, or, xor etc...). They would almost always map to a single instruction in MIPS, ARM32, ARM64, Z80 etc... So I'm not convinced that there's a direct correlation here, one way or the other.
Conversely if C was really deeply influenced by the PDP-11 ISA then why isn't there a single "bit set", "bit clear" or "rotate right" operator in C? Those require multiple operators in C but a single opcode here. Rotation in particular is a bit of a pain to code in C since you need to know the size of the integer you're rotating. I always thought that it was an oversight not to have built-in support for rotations in C.
The only thing that really stands out as being fairly C-ish is the auto-incrementing and -decrementing addressing modes which map well to p++ and friends (although ++p needs two opcodes as far as I can tell) but it's a feature that's available on other ISAs.
Just curious: is there a use case for rotations where the integer size isn't important? It's perhaps a bit annoying to need size-specific rotation functions, but all the uses of rotations I know of need specific integer sizes anyways.
You're right of course, I just mean that it makes it a bit verbose and error-prone to write in full "(i >> (32 - shift)) | (i << shift))". I need to triple check that I'm not off-by-one and that all the operators are in the right direction every time I write this. If you had rotation operators like, say, "<|" you could write "i <| shift" instead and that'd be a lot nicer to read and write IMO.
You would need to look into the addressing modes at length.
* --i = * j++ is directly a «mov @(r0)+, -(r1)» (provided * j is in r0 and * i is in r1) whereas * i++ = * j-- is a «mov @-(r0),@(r1)+»; i[4] = 1 is «mov #1, @4(r1)» and * (int * )040 |= 01 is «bis 01, @#040». A function call via a function pointer (that is, say, stored in r2) is a «jsr pc, @(r2)» etc etc.
Most of the C pointer operations map onto single PDP-11 instructions and can be used even with the program counter register (which is r7).
At least that is what my compilers writing class taught me 25 years ago ;-)