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?
305419896 (signed) · 0x12345678
103 (unsigned) · 0x00000067
0 (signed) · 0x00000000
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:
| instruction | sa holds | rd holds |
|---|---|---|
ext rt,rs,pos,size | pos | size − 1 |
ins rt,rs,pos,size | pos | pos + size − 1 (the field’s top bit) |
ext $t0,$t1,3,4 (0x7D2818C0): sa = 3, rd = 3
Where are pos and size?
ins $t0,$t1,3,4 (0x7D2830C4): sa = 3, rd = 6
Same pos and size. Why is rd different?
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?
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
- 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.
- 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.
- 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 answered1.$t1 = 0x12345678. What does `ext $t0,$t1,4,8` put in $t0?
2.Before release 2, how did a compiler extract bits 11–4 of $t1 into $t0, and what does ext save?
3.What does `clz $t0,$t1` give for $t1 = 1, and for $t1 = 0?
4.$t0 = 0x11223344, $t1 = 0xAB. After `ins $t0,$t1,8,8`, what is $t0?
5.The census of 34 builds found no ext, ins, clz or clo. Why not?