Master the fundamental concepts of instruction set architecture through this focused micro-challenge.
You have read the whole brief, and the concepts above stay free on every task. Writing and running the code needs a plan.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksCISC ISAs (x86) pack rich operations into variable-length encodings to save memory on 1980s machines with tiny RAM. RISC ISAs (ARM, RISC-V, MIPS) favor fixed-width instructions, many registers, and load/store simplicity so decode hardware stays fast and power low.
| Trait | CISC (x86) | RISC (ARM/RISC-V) |
|---|---|---|
| Instruction size | Variable | Fixed 32-bit (mostly) |
| Registers | 16 named GPRs | 31-32 GPRs |
| Memory ops | Arithmetic on memory | Loads/stores only |
Code density favors CISC: the same C function may compile smaller on x86. Clock-for-clock on modern out-of-order cores, RISC designs win on power and decode regularity, not raw instruction count.
For this exercise, you will compile identical loops for two targets and compare static instruction counts and dynamic retired instructions (perf stat). This task asks you to ground ISA debates in measured data instead of 1990s textbook slogans.
Keep the relevant datasheet, ISA manual, or architecture textbook chapter open while you implement. When your output disagrees with the reference trace on the same program, the bug is usually a mis-decoded opcode, a stale register read, or a flag bit left unchanged after arithmetic.
For this exercise, you will use those habits while implementing the requirement in the starter code. Microarchitectural product names change across CPU generations, but the control ideas (fetch, bypass, cache lines, vector lanes) stay stable enough to debug from first principles.
Modern x86 cores don't execute CISC instructions directly. The decoder first cracks each one into simple RISC-like micro-operations. Write that cracker for a small x86 subset. Translate each CISC instruction into load/store-architecture operations, then compare the two programs by instruction count, code size and memory traffic. The comparison shows the CISC/RISC trade-off: x86 is denser, the RISC version has more but simpler instructions, and the memory traffic is identical.
R, R1, R2 are register names (any identifier). M is a memory operand [reg], [reg+N] or [reg-N], written in RISC form as N(reg) (0(reg) when there's no offset). imm is a decimal integer. OP is add or sub. tmp is a scratch register.
| CISC | Bytes | RISC operations |
|---|---|---|
mov R1, R2 | 3 | mv R1, R2 |
mov R, imm | 5 | li R, imm |
mov R, M | 4 | ld R, M |
mov M, R | 4 | sd R, M |
OP R1, R2 | 3 | OP R1, R1, R2 |
OP R, imm | 4 | addi R, R, imm (-imm for sub) |
OP R, M | 4 | ld tmp, M · OP R, R, tmp |
OP M, R | 4 | ld tmp, M · OP tmp, tmp, R · sd tmp, M |
inc M / dec M | 4 | ld tmp, M · addi tmp, tmp, 1 (or -1) · sd tmp, M |
push R | 1 | addi sp, sp, -8 · sd R, 0(sp) |
pop R | 1 | ld R, 0(sp) · addi sp, sp, 8 |
Every RISC operation is 4 bytes. ld and sd are memory accesses. A CISC instruction makes the same accesses: one per ld/sd in its translation.
One CISC instruction per line (lower case, operands separated by , and optional spaces). For each line:
cLoading…
CISC-TEXT is the instruction normalised to mnemonic op1, op2. A line matching no row prints CISC-TEXT -> unsupported and is excluded from the totals. Then:
cLoading…
Input:
cLoading…
Output:
cLoading…
[reg±N] to N(reg) in one helper.Hidden tests cover register-to-register and immediate forms, sub with an immediate, a store, inc/dec on memory, negative offsets, and unsupported forms such as memory-to-memory mov.