Skip to content
Contents

Bit fields and counting: ext, ins, clz, clo, rotr

Section 8.2 9 min read

  • a bit field: pos and size
  • ext: pull a field out; ins: put a field in
  • how pos and size are packed into sa and rd
  • clz and clo

Checked against: MIPS32 Vol. II: EXT, INS, CLZ, CLO, ROTR carolyne_ref/isa_mips/imm.py (imm_bitfield) carolyne_ref/isa_mips/mop.py (MOP_SPECIAL2, MOP_SPECIAL3) carolyne_ref/isa_mips/exec_unit_alu.py (clz as a mux chain)

Before you start

  • §3.3: masks, shifts, rotr
  • §1.3: the sa and rd fields

By the end, you can

  • Compute ext and ins results by hand
  • Say what the assembler stores in sa and rd for each
  • Give clz for any value, including 0
  • Name the shift-and-mask idiom each instruction replaces

A field of bits

A is a run of size bits starting at bit pos. Two release-2 instructions work on one directly. Drag the sliders and watch the bracket move.

ext and ins on a field of 0x12345678

What do the two instructions do with the bracketed bits?

bit 31bit 27bit 23bit 19bit 15bit 11bit 7bit 3
rs (the source)the field: bits 11..4

305419896 (signed) · 0x12345678

ext rt,rs,4,8: the field moved down to bit 0, zeros above

103 (unsigned) · 0x00000067

rt before ins (the destination keeps its other bits)

0 (signed) · 0x00000000

ins rt,rs,4,8: the low 8 bits of rs placed at bits 11..4 of rt

1920 (signed) · 0x00000780

  • the selected field
  • bits written by the instruction
  • zeros added by ext

Click any input bit to flip it.

What to notice

ext copies the bracketed bits of rs down so they start at bit 0, and zeros everything above. ins does the reverse: it takes the low bits of rs and writes them into the bracket of rt, leaving rt’s other bits exactly as they were.

replaces srl + andi: shift down, then mask. replaces four instructions: clear the field of rt with a mask, shift the new bits up, mask them, OR them in. And because ins keeps the other bits of rt, the old rt is one of its inputs, like the old rd of movz (§8.1).

How the word stores pos and size

ext and ins need two numbers, but an R-format word has only the 5-bit sa and rd fields free. The assembler packs them:

instructionsa holdsrd holds
ext rt,rs,pos,sizepossize − 1
ins rt,rs,pos,sizepospos + size − 1 (the field’s top bit)

ext $t0,$t1,3,4 (0x7D2818C0): sa = 3, rd = 3

Where are pos and size?

3126
opcode6b
011111
31 = SPECIAL3
2521
rs5b
01001
$t1 ($9)
2016
rt5b
01000
$t0 ($8)
1511
rd5b
00011
3 = size − 1
106
sa5b
00011
3 = pos
50
funct6b
000000
0 = ext
binary01111101001010000001100011000000
hex0x7d2818c0one hex digit = four bits; a digit that spans two fields has two colors
asmext $t0,$t1,3,4R-format
Hover or focus a field to see its bits, value and role.

ins $t0,$t1,3,4 (0x7D2830C4): sa = 3, rd = 6

Same pos and size. Why is rd different?

3126
opcode6b
011111
31 = SPECIAL3
2521
rs5b
01001
$t1 ($9)
2016
rt5b
01000
$t0 ($8)
1511
rd5b
00110
6 = pos + size − 1
106
sa5b
00011
3 = pos
50
funct6b
000100
4 = ins
binary01111101001010000011000011000100
hex0x7d2830c4one hex digit = four bits; a digit that spans two fields has two colors
asmins $t0,$t1,3,4R-format
Hover or focus a field to see its bits, value and role.

What to notice

Both are SPECIAL3, opcode 31; funct 0 is ext, 4 is ins. sa = 3 is the position in both. For ext, rd = 3 is size − 1; for ins, rd = 6 is the top bit of the field, pos + size − 1. You write 3,4 either way; the assembler and the disassembler do the arithmetic.

Carolyne’s description packs the two fields into one immediate (imm_bitfield places sa at bit 0 and rd at bit 8), so ins still fits the three-source limit: rs, the old rt, and the packed pair.

Before reading on, decide:

$t1 = 0x12345678. What does `ext $t0,$t1,8,4` give, and what does the assembler store in rd?

Counting leading zeros

counts the zero bits at the top of rs, from bit 31 down to the first 1. clo counts leading ones. For 0, clz gives 32.

clz of 0x00001000

How many zeros before the first 1, and what is 31 minus that?

bit 31bit 27bit 23bit 19bit 15bit 11bit 7bit 3
rsclz = 19 leading zeros

4096 (signed) · 0x00001000

clz counts from bit 31 downward until the first 1. For zero it returns 32. clo does the same for leading ones: clo(rs) = 0.

  • the leading zeros that are counted

Click any input bit to flip it.

What to notice

Nineteen leading zeros, then the 1 at bit 12: clz = 19, and 31 − 19 = 12 is the position of the highest set bit, the integer log₂. Click bits to move the first 1 and watch the count follow. All-zero gives 32.

clz is what __builtin_clz compiles to. It turns up in normalisation, integer logarithms and finding the highest set flag. None of the reference programs needed it: in the 34-build census ext, ins, clz and clo appear zero times. They are in the set because the compiler can emit them.

rotr, again

rotr and rotrv are release 2 too, and §3.3 covered them: a right shift whose fallen bits come back at the top, encoded like srl/srlv with the rs or sa field set to 1. The word 0x00294102 is rotr $t0,$t1,4; with rs = 0 it would be srl $t0,$t1,4.

Go deeper: how Carolyne runs these four, and why clz is a chain of muxes

ext and ins are ALU µops. The body reads pos and size − 1 (or the top bit) out of the packed immediate, builds the mask (1 << size) − 1, and computes (rs >> pos) & mask for ext or (rt & ~(mask << pos)) | ((rs << pos) & (mask << pos)) for ins, with variable shifts. The old rt of ins arrives as a source operand, the same trick as movz’s old rd.

clz has no shifter form. The body is a 32-step mux chain: starting from the top bit, each step asks “is this bit the first 1?” and selects its position. It is the longest ALU path after the shifters, and a real implementation would use a tree instead of a chain. clo is the same chain on the inverted input. Both have their own uop_idx and are decoded under SPECIAL2 by funct 32 and 33.

Can you now explain it?

A bit field is pos and size. ext cuts it out and moves it to the bottom; ins pastes into it and leaves the rest. The assembler packs pos and size into sa and rd. clz counts the zeros at the top, 32 for zero.

If you remember only three things

  1. ext is srl + andi; ins is four instructions folded into one. Both name the field as pos,size in assembly and store it as sa, rd in the word.
  2. ins and movz read their destination. An in-place edit keeps the untouched bits, so the old value is an input; Carolyne lists it as a source.
  3. clz gives the position of the highest 1 by subtraction. 31 − clz(x) is the integer log₂ of x, and clz(0) = 32 is the empty case.

Now try to explain this one yourself: a byte is stored in bits 23..16 of $t1. Write the release-2 way and the MIPS I way to get it into $t0 unsigned. Then do the same for a signed value.

Go deeper: a model answer, once you have tried

Unsigned, release 2: ext $t0,$t1,16,8. MIPS I: srl $t0,$t1,16 then andi $t0,$t0,0xff. Signed, release 2: the same ext, then seb $t0,$t0 to spread bit 7 (§8.1). MIPS I: sll $t0,$t1,8 to put the byte at the top, then sra $t0,$t0,24 to bring it down with its sign filled in. Two instructions either way for the signed case, but the release-2 pair reads as what it means: extract, then sign-extend.

Check your understanding

0/5 answered
  1. 1.$t1 = 0x12345678. What does `ext $t0,$t1,4,8` put in $t0?

  2. 2.Before release 2, how did a compiler extract bits 11–4 of $t1 into $t0, and what does ext save?

  3. 3.What does `clz $t0,$t1` give for $t1 = 1, and for $t1 = 0?

  4. 4.$t0 = 0x11223344, $t1 = 0xAB. After `ins $t0,$t1,8,8`, what is $t0?

  5. 5.The census of 34 builds found no ext, ins, clz or clo. Why not?


Next: Chapter 9 — the toolchain and pseudo-instructions.

Where this comes back

One later section builds on this one.