Search code examples
assemblymipsaddressing-mode

What is the function of parentheses in MIPS?


I've been working through a project book as an introduction to MIPS and I've come across a problem. One of the lines of code in the book is lb $t3, ($t2). I have no clue what the parentheses do because prior to this, I haven't seen them used, and the book just doesn't mention them to begin with. Why wouldn't the code just be lb $t3, $t2?


Solution

  • MIPS addressing-mode syntax is constant($reg).

    ($t2) is allowed as a special case short-hand for 0($t2). The same instruction could do
    lb $t3, 13($t2) to load (and sign-extend) a byte from memory at address 13 + $t2 into register $t3.

    MIPS's only addressing mode is reg + sign_extended_imm16; load / store instructions (including lb and lbu) are I-type. Omitting the 0 in 0($t2) is just a source-level syntax nicety; the machine code still has 16 zero bits to encode that constant zero.

    BTW, I'm ignoring the existence of MIPS's indexed FP load/store instructions like lwxc1 that use 2 integer registers. They're FP-only perhaps because an integer store version would need 3 GP registers as inputs, but that register file otherwise only needs 2 read ports (for a scalar pipeline). (And the architects apparently thought symmetry between loads and stores was more important than providing 2-register addressing modes for loads only; that would have been fine and useful for lots of code).
    There's also lwpc for PC-relative addressing in some newer MIPS revisions. But lw / lb / lbu and so on have no bits to encode what type of addressing mode; it's always reg + imm16. Anything else requires a different instruction, and classic MIPS didn't have anything else.


    The semantic meaning (for human readers of asm text syntax) is a dereference operation, like in C int t3 = *t2; or t2[0] loads the pointed-to value (lb) instead of int tmp = (int)p copying the pointer as an integer (move).

    () is always a memory operand and can only be used with load or store instructions. Bare registers are not memory addressing modes, and can't be used as the 2nd operand to load or store instructions.

    It's a good thing that load / store instructions require parens on the addressing mode, so you don't get mixed up on which operand is the address and which is the value. e.g. sw $t3, 12($t2) stores the word in $t3 into a word of memory at the address 12+$t2. If sw $t3, $t2 was valid syntax, you might forget that the address is always on right for memory instructions on MIPS (like most RISCs), even though every other instruction has the destination as the first operand.

    This makes it more visually distinct from move $t3, $t2. Spotting the loads and stores in a block of code is nice to be able to do visually.

    If you were designing your own syntax for MIPS assembly language, or writing an assembler, you could make it legal to write lb $t3, $t2 as a short-hand for lb $t3, 0($t2). But asm syntax is defined by (the authors of) the assembler, and the designers of MIPS syntax decided not to do that.


    prior to this, I haven't seen them used, and the book just doesn't mention them to begin with.

    Keep reading; hopefully the book goes on to explain new syntax some time after first introducing it. It's totally normal for a tutorial or book to show you some code as an example without stopping to explain everything, especially when the explanation is based on concepts it hasn't got to yet.