I'm learning how to convert RISC-V assembly code to C, and I don't understand this conversion. A few questions I have:
t1
being initialized to 6
instead of 0
?while (t1 == t0)
. It appears this opposite technique was used for if (t1 ==0)
. Why?addi t0, t0, -1
used instead of sub?Any insight is so greatly appreciated. My classroom environment is fast paced and not very question friendly, so I'm nervous to ask in lecture.
main:
# Tests simple looping behavior
li t0, 60
li t1, 0
loop:
addi t1, t1, 5
addi t0, t0, -1
bne t1, t0, loop
bne t1, zero, success
failure:
li a0, 0
li a7, 93
ecall
success:
li a0, 42
li a7, 93
ecall
This is the answer I was given:
int main(){
int t0 = 60;
int t1 = 6;
while(t1 != t0){
t1 = t1 + 5;
t0 = t0 - 1;
}
if(t1 == 0){
int a0 = 0;
return 0;
}else{
int a0 = 42;
return 0;
}
}
We were taught to use the opposite of bne/beq when converting C to RISC-V, so it's confusing why the 'correct' C conversion for this RISC-V assembly would include while (t1 != t0)
.
Initializing t1 to 6 also doesn't make any sense to me. It looks to be clearly loaded to 0 with 'li t1, 0'.
The C doesn't match the RISC-V assembly very well.
You're right, the asm clearly does t1 = 0
not t1 = 6
. Perhaps a typo or OCR error if this material was printed out and scanned back in or something. Or maybe someone just changed their mind about the starting point for the loops but forgot to update one of the versions. 0
and 6
do both lead to the loop terminating without wrapping around (or actually C signed-overflow undefined behaviour since this isn't unsigned
.)
Also, those ecall
s are _exit(0)
and _exit(42)
, not return
statements. The asm doesn't use its return address.
The rest looks right, though. Try it yourself, single-stepping through the asm vs. through the C program, watching registers or variables change, respectively. It should be clear that execution stays in the loop until they're equal, i.e. while they're not equal.
The most direct C representation for an idiomatic asm loop like that is do{}while(t1 != t0);
with the condition at the bottom like the asm has. (Showing it as a for
or while
loop depends on the initializers making the condition true so the loop body runs at least one iteration.) See Why are loops always compiled into "do...while" style (tail jump)?
(3) Try changing the asm to use subi
with an immediate 1
. Most assemblers will reject that because RISC-V doesn't have subi
, except maybe as a pseudo-instruction. It doesn't have a FLAGS / condition-code register, so subtracting is exactly equivalent to adding a negative, and RISC-V always sign-extends immediate operands. The only thing it would gain from a subi
opcode is being able to change the value by +4096 .. -4095
instead of addi
's range of -4096 .. +4095
with its 12-bit immediate. That's obviously not worth having another opcode for.
MIPS is very similar to RISC-V in this way (no FLAGS and not having a hardware subi
); see What is the "relationship" between addi and subi? about that and the ISA vs. software assembler pseudo-instruction design choices.