Every instruction set is a collection of design decisions: how long instructions are, how many registers there are, whether arithmetic can read memory directly, how complex a single instruction may be. Those decisions were made under the constraints of their time and still shape how processors are built today. This article explains the two great families, CISC and RISC, compares the same C loop compiled for x86 and for RISC-V instruction by instruction, measures what RISC-V's compressed extension does for code size, and puts a number on what an extension costs in silicon by synthesizing a hardware multiplier.
13 min read | Beginner to Intermediate
What You'll Learn
- Describe the main design choices of an ISA and the trade-offs behind each
- Compare CISC and RISC instruction sets and explain how each came about
- Explain code density and what compressed instructions save
- Weigh the hardware cost of an extension against what it gains
The Choices Behind an ISA
An ISA designer decides, among other things:
- Instruction length: all instructions the same size (fixed length), or each as long as it needs (variable length).
- Operands: how many registers an instruction names (two, with the result overwriting one source, or three), and how many registers exist.
- Memory access: whether only loads and stores touch memory, or arithmetic instructions can read and write memory too.
- Addressing modes: one simple way to form an address, or many.
- Condition codes: whether arithmetic sets flags that branches test, or branches compare registers themselves.
- Instruction complexity: only simple operations, or also complex ones such as copying a whole string in one instruction.
None of these has one right answer. Each trades the size of the program against the complexity of the hardware, and the balance depends on what is expensive at the time.
CISC: Doing More per Instruction
In the 1970s, memory was expensive and slow, and compilers were simple. An ISA that packed a lot of work into each instruction saved memory and let programmers, who often wrote assembly by hand, express a task in few lines. These complex instruction set computers (CISC), such as the DEC VAX, the Motorola 68000 and Intel's x86, had variable-length instructions, arithmetic that could read and write memory directly, many addressing modes, and powerful instructions for strings, loops and procedure calls.
Inside, such processors often executed each complex instruction as a sequence of simpler internal steps, stored in an on-chip ROM as microcode. The hardware stayed manageable, and new complex instructions could be added by writing more microcode.
RISC: Simple Instructions, Fast
By around 1980, researchers measuring real programs noticed that compilers used mostly the simple instructions, and that the complex ones were rare and often slower than an equivalent sequence of simple ones. At the same time, pipelining promised to start one instruction per cycle, but only if instructions were regular. The result was the reduced instruction set computer (RISC): fixed-length instructions with fields in fixed places, a load/store architecture, many registers (usually 32), three-operand arithmetic and simple addressing. The first RISC designs at Berkeley and Stanford led to SPARC and MIPS; IBM's work led to POWER; and Arm and, later, RISC-V followed the same principles. RISC-V itself grew out of the Berkeley RISC tradition.
The bet was that simpler instructions would let the hardware run faster and pipeline easily, and that a good compiler could make up for needing more of them. The Performance Equation gives the exact form of that trade: run time is instructions x cycles per instruction x clock period, and RISC accepts more instructions to lower the other two.
The Same Loop on x86 and RISC-V
Take a loop that adds up a 1,024-element array. Compiled with optimization for x86-64 (with automatic vectorization switched off, so that the loop stays one element at a time) and for RV32I, its inner loop is:
x86-64 RV32I
38: 03 10 add (%rax),%edx 44: 00072783 lw a5,0(a4)
3a: 48 83 c0 04 add $0x4,%rax 48: 00470713 addi a4,a4,4
3e: 48 39 c8 cmp %rcx,%rax 4c: 00f50533 add a0,a0,a5
41: 75 f5 jne 38 50: fec71ae3 bne a4,a2,44
4 instructions, 11 bytes 4 instructions, 16 bytes
The x86 loop shows several CISC traits at once. Its first instruction, add (%rax),%edx, reads memory and adds in a single 2-byte instruction, where RISC-V needs a load and an add. Its instructions are 2, 4, 3 and 2 bytes long; elsewhere in the same function they reach 7 bytes. Its arithmetic has two operands, with the result overwriting one of them. And its branch tests flags set by the cmp before it, where RISC-V's bne compares the registers itself.
Yet both loops have four instructions. x86 saves the separate load, RISC-V saves the separate compare. The real differences are in bytes and in how hard the instructions are to decode: the x86 decoder must find where each instruction ends before it can work on the next, while every RV32I instruction starts at a multiple of 4 bytes with its fields in the same places.
Quick Check
Q: Why must an x86 processor that decodes four instructions per cycle do more work than a RISC-V one, even before executing anything?
Show Answer
x86 instructions have variable length, so the position of the second instruction depends on the length of the first, and so on. The decoder has to determine lengths, often by examining many possible starting bytes in parallel. With fixed 4-byte instructions, every instruction's start is known in advance.
The Lines Have Blurred
Modern x86 processors do not execute x86 instructions directly. Their front end translates each one into simpler internal operations, usually called micro-operations, and the rest of the core is essentially a very large RISC-like machine. Arm, for its part, added compressed 16-bit instructions (Thumb) to improve code density, then designed its 64-bit ISA with fixed 32-bit instructions again. RISC-V keeps a fixed-length base but offers a compressed extension. The lesson of four decades is that both ends of the trade matter: simple, regular instructions for the hardware, and compact code for memory and caches.
Code Density and the C Extension
Code density is how much program fits in a given number of bytes. It matters wherever instruction memory is costly: in a microcontroller's flash, and in every processor's instruction cache, where denser code means more of the program fits and fewer fetches go to slower memory.
The RISC-V C extension gives the most common instructions an alternative 16-bit encoding. It is possible because those instructions often use only a few registers, small constants, or a destination equal to a source: addi a4, a4, 4 fits in 16 bits as c.addi, add a0, a0, a5 as c.add. Each 16-bit instruction expands to exactly one 32-bit instruction, so C adds no new operations. The same loop compiled for rv32ic:
34: 431c lw a5,0(a4) 16-bit
36: 0711 addi a4,a4,4 16-bit
38: 953e add a0,a0,a5 16-bit
3a: fec71de3 bne a4,a2,34 32-bit (a branch between two registers has no 16-bit form)
4 instructions, 10 bytes: smaller than the 11 bytes of x86 for this loop
In one test of six small programs, about half of the instructions got a compressed form, and the code shrank by a fifth to more than a quarter.
The price is paid in the processor's front end. Instructions are now aligned to 2 bytes instead of 4, so a 32-bit instruction can straddle two fetched words, and the fetch logic must collect its halves. The decoder needs an expander that turns each 16-bit form into its 32-bit equivalent. For small cores with tight flash budgets the trade is clearly worth it, which is why nearly every RISC-V microcontroller implements C.
What an Extension Costs
Extensions are optional because they cost hardware. The M extension's mul needs a multiplier. In one test, a 32 x 32-bit multiplier that returns the low 32 bits of the product, exactly what mul computes, was built from the same gate library as a small five-stage RV32I core.
That single-cycle multiplier came to about 22 percent of the core's area, roughly a fifth more hardware. A slower multiplier that takes several cycles would be smaller. Against that stands the gain: a matrix program needed about a third less code with M, and at run time each multiplication shrinks from a call into a shift-and-add routine of dozens of instructions to a single mul. For a program that multiplies constantly, the extension pays for itself many times over; for one that never multiplies, it is pure cost.
Designers can also choose a middle way. A multi-cycle multiplier that processes a few bits per cycle is much smaller and still far faster than the software routine. The ISA does not care: mul must give the right result, and how many cycles it takes is a microarchitecture decision. This is exactly the freedom described in What an ISA Defines.
Where Does This Matter?
Companies that build RISC-V chips choose extensions per product: a sensor controller may take RV32EC for the smallest area, a motor controller RV32IMC with a single-cycle multiplier, an AI chip may add custom instructions for its matrix operations. Arm offers similar choices through its range of cores and optional features. Architects decide with exactly the kind of numbers shown here: what the extension costs in area and power, and what it saves in code size and run time on the product's own software.
Custom Extensions and the Limits of Adding
RISC-V reserves encoding space for custom instructions, so a company can add operations specific to its product without colliding with standard ones. A custom instruction can replace a hot loop of dozens of instructions with one, which is attractive for accelerating a particular algorithm. But every added instruction must be supported by the compiler, debugger and simulators, verified in every corner case, and kept forever if software depends on it. The history of CISC shows the risk: instructions added for one generation's needs remain a burden decades later. Good ISA design adds only what measurements show to be worth it.
Quick Check
Q: The C extension adds no new operations and the M extension does. Which of the two changes how many instructions a program executes?
Show Answer
M. Every compressed instruction is one ordinary instruction in a shorter encoding, so C changes code size but not instruction count. M replaces whole software routines with single instructions, so programs that multiply or divide execute far fewer instructions.
Common Mistakes
Mistake #1: Assuming RISC means fewer instructions per task
RISC usually needs as many or more instructions than CISC for the same task; the sum loop needs four on both. Its advantage is that each instruction is simpler to decode and pipeline, not that there are fewer of them.
Mistake #2: Treating code size as unimportant
Code size decides flash cost in microcontrollers and instruction-cache hit rates in large cores. A 25 percent saving can mean a smaller chip or a faster one.
Mistake #3: Judging an extension by its instructions alone
An extension costs area, power, verification and software support. Compare its hardware cost with measured gains on the software the product will actually run.
Practice Exercise
Challenge: Is the Multiplier Worth It?
A product runs a filter that performs 2,000 multiplications per millisecond. Without the M extension, each multiplication is a call to __mulsi3, which costs on average 120 cycles including the call and return (an assumption for this exercise); with a single-cycle multiplier, each costs 1 cycle. The core runs at 50 MHz. (a) How many cycles per millisecond does the filter's multiplication take without and with M? (b) What fraction of the processor's time is that in each case? (c) The multiplier adds about 22 percent to the core's area. Would you add it, and why?
Hint
At 50 MHz, one millisecond is 50,000 cycles.
Solution
(a) Without M: 2,000 x 120 = 240,000 cycles per millisecond. With M: 2,000 x 1 = 2,000 cycles. (b) Without M the processor would need 240,000 / 50,000 = 4.8 times its whole capacity: it cannot keep up at all. With M it uses 2,000 / 50,000 = 4 percent. (c) Yes: without the multiplier the product does not work at this clock, and the alternative, a much faster clock or a bigger core, would cost far more than 22 percent of a small core's area. For software that never multiplies, the same multiplier would be wasted area.
Summary
- An ISA fixes instruction length, operands, memory access, addressing modes, condition codes and instruction complexity, each a trade between code size and hardware simplicity.
- CISC packs more work into variable-length instructions; RISC uses simple, fixed-length, load/store instructions that pipeline easily.
- The sum loop is 4 instructions in both x86-64 (11 bytes) and RV32I (16 bytes); with RISC-V compressed instructions it is 10 bytes.
- The C extension compresses about half of the instructions and saved a fifth to a quarter of code size in one test.
- A single-cycle 32-bit multiplier for the M extension can add about a fifth to the area of a small core.