myfidan
myfidan

Reputation: 17

MIPS assembly: instruction "beq" and the 'and' operator

Can I use the 'and' operator with 'beq' in MIPS conditional statements?

For example, in C, I can write if(arr[0]=='a' && arr[1]=='b' && arr[2]=='c'), but how can I write like this code in MIPS assembly?

Upvotes: 0

Views: 3042

Answers (3)

Erik Eidt
Erik Eidt

Reputation: 26646

Fundamentally, we combine flow of control with logic of the conditions we're testing.  As Jester is telling you, we can do/observe this in C, which is often friendlier for these transformations than assembly.

You can see that:

if(arr[0]=='a' && arr[1]=='b' && arr[2]=='c') { ... }

is equivalent to:

if (arr[0]=='a') {
    if (arr[1] == 'b') {
        if (arr[2] == 'c') {
            /* ... */
        }
    }
}

So, if you know how to do if (x == y) { ... } then just apply that three times.


In assembly language our only decision control flow construct is "if-goto", and of course only simple conditions can be tested.

So, to do if (x==y) { then-part } else { else-part }, using an "if-goto" style, we test x==y and upon that condition being false we branch around the then-part and to the else-part.  Since we are branching on condition false, then when the condition is true, we fail to branch and run the then-part that we program immediately following the condition test.

Since we're branching on condition false, then for all practical purposes, we write, in "if-goto" style in C: if (x!=y) goto ElseLabel; followed by the then the then-part translation...


For reference, see the following posts:

Upvotes: 3

CrustyCrustecean
CrustyCrustecean

Reputation: 30

No, you would instead just do two beq instructions to account for both conditions.

Upvotes: 0

Peter Cordes
Peter Cordes

Reputation: 364248

No, beq takes 2 registers and compares them, that is all. Remember, assembly language reflects what machine code can do in one instruction. Doing something more complicated often requires more instructions.

To && multiple conditions together, you need either

  • multiple beq and/or bne instructions; a chain of branches

  • or create a value in one register that represents the logical and of multiple conditions before branching. For example, load all 3 bytes and xori them with 'a', 'b', and 'c' respectively. (Producing 0 for a match). Then or those results together and see if the final result is 0 (with a bne or beq against $zero) If so, there were no mismatching bits in any of the 3 bytes so the condition is true.

The 2nd way optimizes away the short-circuit eval that the C logic has. Note that in the C expression, a[1] isn't even accessed if a[0] != 'a', so it wouldn't fault even if a was a pointer to the last byte of a page, and the next page was unmapped. (Assuming a[0] == 0 or something, an empty 0-terminated string).

But if you do know you can safely access all 3 bytes of the string/array, this is an option.

It's easier / more effective as an optimization when the condition is something like if (x < 5 && y < 10) which you can implement with 2x slti instructions to compare into registers, AND those regs together, then beq $t0, $zero, skip_if_body


In this special case where you're checking 3 contiguous bytes, it's basically memcmp(a, "abc", 3).

If you know that a is word-aligned, you can do a word load to get the 3 bytes you want, plus one byte of garbage we need to ignore.

MARS simulates a little-endian MIPS system, so the 3 bytes we want are the 3 least-significant bytes in the word. (In general MIPS can run as big or little-endian.)

# assuming a[] is a word-aligned static array
# and little-endian MIPS

    lw   $t0,  a             # pseudo-instruction for lui / lw to construct the full address
    li   $t1,  'abc' << 8    # 0x63626100  if your assemble doesn't like multi-char literals

    sll  $t0, $t0, 8         # shift out the 4th byte which we need to ignore
    bne  $t0, $t1, skip_if_body
         # if body: a[0] == 'a' && a[1] == 'b' && a[2] == 'c'
         ...
skip_if_body:
    ...
    jr $ra

You can of course also use this if a is actually a pointer in a register which is known to be word-aligned. lw $t0, ($a0).

I shifted the unwanted byte out of the load instead of masking it with AND because andi can't encode 0x00FFFFFF.

If a wasn't known to be aligned, possibly lwl/lwr for an unaligned load would have been worth using.

Or lhu + lbu to load 16 bits and 8 bits separately if we have 2-byte alignment; we can check 16 bits at once with xori making use of the full width of the immediate, if we still want to combine for one branch. Or just with li/beq.

Constructing 'abc' << 8 in a register takes 2 instructions (lui + addiu or ori)

Upvotes: 0

Related Questions