8 ms·
Turns out that they were a good fit for the kind of rasterization loops that you find in Quake and Doom. E.g. https://gitlab.com/mbitsnbites/mc1-quake/-/blob/fe
by mbitsnbites 3y ago
Turns out that they were a good fit for the kind of rasterization loops that you find in Quake and Doom. E.g. https://gitlab.com/mbitsnbites/mc1-quake/-/blob/feature/port_to_mc1/WinQuake/d_scan_mr32.s?ref_type=heads#L175 https://gitlab.com/mbitsnbites/mc1-quake/-/blob/feature/port...
They were also really simple to implement in hardware (basically just a small counter that iterates over vector elements while stalling the CPU frontend).
I have not yet scaled up the parallelism internally (by adding more execution units), but I still see performance benefits (less loop logic & branch overhead, less scalar register pressure, less I$ pressure).
- peterfirefly 3y agoCan your compiler generate code that uses them productively or is that an assembly-only thing?
- mbitsnbites 3y agoRight now: assembly only. I don't really know compiler internals so it's an uphill battle.
- peterfirefly 3y agoAnd how do the vectors work with branch prediction? If the CPU speculatively starts executing a vector instruction and it turns out it shouldn't, there should be a mechanism to abort the vector sequencing (or nullify the writebacks while letting the vector sequencing play out). I don't see that in the overview here, but I guess it's just a low-level detail that isn't drawn: https://gitlab.com/mrisc32/mrisc32-a1/-/raw/master/mrisc32-a1-pipeline.png https://gitlab.com/mrisc32/mrisc32-a1/-/raw/master/mrisc32-a...
- mbitsnbites 3y agoI have dodged most problems with speculation by executing the branch early, before the next instruction can enter the execute stage. That's one of the reasons why I have very simple branch conditions. E.g. I don't have the MIPS / RISC-V "branch if two registers are equal/not equal/...", only "branch if register is zero/negative/positive/..." - which saves gate delay (if ever so slightly), and makes it possible to invalidate all the following instructions in the pipeline without needing a branch delay slot or advanced speculation logic for instructions that have already entered the EX stages. In the diagram, as much work as possible is done in "Branch 1" (before register content is available), e.g. calculate the branch target address. All that is done in "Branch 2" is to compare the register value to fixed values (zero and -1) and check the most significant bit (sign bit), and based on the branch condition (from the opcode) determine if the branch was taken or not. (There is some extra trickery going on in order to handle branch prediction - mostly in parallel to evaluating the branch condition) Thus, the branch misprediction penalty is three clock cycles.
- uis 3y agoRead EDIT first. But why not just do branch delay? Or some sort of prepared branches like on ELBRUS where you can write to predicate register many instrucions ago and take conditional branch at same cost of non-conditional or maybe even prefetch intstructions into second copy of front-end like E2K does. Or some sort of explicit branch slots. Anyway, I registered here just to reply to you. I kinda want to talk to you more because you do what is interesting for me too. EDIT: I see you already store "predicates" in registers.
- peterfirefly 3y agoHe hangs out in the comp.arch newsgroup.
- phire 3y agoMy understanding: Delay slots only make sense when you don't have a branch predictor. I get the impression that many people massively underestimate how powerful branch prediction is. Even a simple 2 bit saturating counter will correctly predict about 90% of branches, and the accuracy only goes up with better designs. So why optimise for the uncommon case of incorrectly predicted branches? In the worst case with static branch delay slots, it actually harms branch prediction, because instead of executing the correctly predicted instructions, it's executing the delay slot, which is often a nop because the compiler couldn't find something to put there. With your other ideas (multiple decoders, explicit delay slots) it's just a question about if it's a good use of resources (design time, transistors, compiler support) to support this uncommon case, or if you might be better off optimising something else like the branch predictor so more code goes down the common path, or just improving the pipeline's throughput in general.
- peterfirefly 3y ago> Delay slots only make sense when you don't have a branch predictor. Agreed. I'll go even further: lots of other RISC ideas only really matter when you don't have a branch predictor (and your transistor and design budgets are tiny). Single-length instructions, for example. Even x86 instructions aren't much of a problem when you can afford to throw more pipeline stages at the problem -- which you can with a branch predictor. I don't think anybody in the mid-80's understood how good branch predictors can be. Most people probably still didn't really understand it in the early 90's.