It's not very RISC purist; the designers seem to have been happy to make an instruction do more than one thing,
Erm, RISC isn't about doing only one thing, RISC is about not doing complex stuff. RISC is not about the instruction set, but simplifying the CPU, as a simple CPU can be speed up much better than a complex.
It is not doing away with powerful operations, but stripping the instruction set of all the stuff that makes CPU construction complex. Foremost of instructions that need long pipelines, like to wait for partial results of an operation, like fetching operands or storing results (or both). The goal is to have an instruction set that can, for the most part, executed with a simple 2-3 stage pipeline without need for logic to handle dependencies.
For example, every instruction (not just branch) is predicated on condition codes; every instruction can optionally shift the output;
All of that are not complex instruction, but application of the VLIW principle. VLIW can easy be seen as the pinnacle of RISC as it does even away with most of the decoding stage in favour of direct control of all available function units.
there are instructions to save and restore multiple registers.
While this is an instruction 'looping' around the registers, it's as well very essential for function and task switching, thus not burdening the transfer of register content with an equal number of instruction fetches makes a huge difference. Such is found in many RISC architectures. RISC is all about improving, not being stupid.
One thing it doesn't have, is compare and branch in one instruction; you need to compare values first (setting a condition code), then branch on that condition code. This is admittedly similar to many early microprocessors, but to my way of thinking, it misses an opportunity when your instruction words are always 32 bits; that's plenty enough bits to specify a pair of registers to compare, and a branch displacement, in one instruction, which would save both cycles and code size.
This is a very CISCy instruction - and an unnecessary one:
- It does not - and CAN not - save execution time, as the second part (branching) is dependent on the first part, so it always needs two cycles (at least)
- To get execution (somewhat) down, it would require another pipeline stage, a branching stage after the operation stage. Worst requirement one can do to a RISC design.
- It would not replace any existing branch or compare, but essentially duplicate those instructions. Rather bloaty, isn't it?
- It would need not only the opcode, registers to compare and target, but additional
- 4 bits for the condition to check for and
- 4 bit for an optional Link register
- There is no room
- CMP already fills all 32 bit with useful values
- B/BL also uses all 32 bits.
Any instruction of that type could, compared with the existing instructions only provide a reduced functionality for either function, while at the same time not eliminating additional branches for complex conditions, all of that at increased execution time.
In contrast, ARM's use of conditional operation does not only away with very common cases of chained operations but also transforms all basic program flow instructions (Jump/Call/Return) into conditional versions. Very lean.
As above, it wasn't about philosophical purity.
I beg to differ, the original ARM was very much a pure concept - maybe even more pure than many other RISC implementations.
One possibility that occurs to me: maybe with the then-available process technology, it wouldn't be possible to run a pair of values through the ALU, and also branch, in the same time; maybe the comparison would have added a cycle to the time taken for a conditional branch. In that case, squeezing them into one instruction, might have helped code density, but it would have done nothing for speed.
Is that the case? Or was there some other reason for the design as it was?
Compare-And-Branche are ta perfect example of CISC bells and whistles, all created to save a tiny bit of code at high implementation cost. Useful only in very limited cases and when memory was as small as in the 1960s. Exactly the kind of instructions RISC was about to cut away. So why on eath would anyone want to include them?
pc
is a general-purpose register and can be read or written like any other. So relative branch could have just beenadd pc, pc, #disp
, and like you say, conditional branches would have come for free. But you really, really want those 8 bits that specify the register for an address instead, so they did special-case a branch instruction, effectively add-immediate with the source and destination registers hardcoded topc
, and a wider immediate.