I needed to translate the follwing C code to MIPS64:
#include <stdio.h>
int main() {
int x;
for (x=0;x<10;x++) {
}
return 0;
}
I used codebench to crosscompile this code to MIPS64. The following code was created:
.file 1 "loop.c"
.section .mdebug.abi32
.previous
.gnu_attribute 4, 1
.abicalls
.option pic0
.text
.align 2
.globl main
.set nomips16
.set nomicromips
.ent main
.type main, #function
main:
.frame $fp,24,$31 # vars= 8, regs= 1/0, args= 0, gp= 8
.mask 0x40000000,-4
.fmask 0x00000000,0
.set noreorder
.set nomacro
addiu $sp,$sp,-24
sw $fp,20($sp)
move $fp,$sp
sw $0,8($fp)
j $L2
nop
$L3:
lw $2,8($fp)
addiu $2,$2,1
sw $2,8($fp)
$L2:
lw $2,8($fp)
slt $2,$2,10
bne $2,$0,$L3
nop
move $2,$0
move $sp,$fp
lw $fp,20($sp)
addiu $sp,$sp,24
j $31
nop
.set macro
.set reorder
.end main
.size main, .-main
.ident "GCC: (Sourcery CodeBench 2012.03-81) 4.6.3"
To check if the code works as expected, I usually use the WINMIPS64 simulator. For one or other reason this simulator does not want to accept this code. It appears that every line of code is wrong. I have been looking at this issue for over a day. I hope someone can help me out with this. What is wrong with this assembly code for the mips64 architecture?
From page 7 of the WINMIPS64 documentation:
The following assembler directives are supported
.data - start of data segment
.text - start of code segment
.code - start of code segment (same as .text)
.org <n> - start address
.space <n> - leave n empty bytes
.asciiz <s> - enters zero terminated ascii string
.ascii <s> - enter ascii string
.align <n> - align to n-byte boundary
.word <n1>,<n2>.. - enters word(s) of data (64-bits)
.byte <n1>,<n2>.. - enter bytes
.word32 <n1>,<n2>.. - enters 32 bit number(s)
.word16 <n1>,<n2>.. - enters 16 bit number(s)
.double <n1>,<n2>.. - enters floating-point number(s)
Get rid of everything that's not in the above list, as it won't run in the simulator.
You'll need to move the .align to before .text
WINMIPS64 expects daddi/daddui instead of addi/addiu, again as per the documentation.
As per the documentation, move $a, $b is not a supported mnemonic. Replace them with daddui $a, $b, 0 instead.
slt needs to be slti.
Finally, the simulator expects an absolute address for j, but you've given it a register. Use jr instead.
At this point I get an infinite loop. This is because the stack pointer doesn't get initialized. The simulator only gives you 0x400 bytes of memory, so go ahead and initialize the stack to 0x400:
.text
daddui $sp,$0,0x400
Now it runs. Since you're running the code by itself, nothing will be in the return register and the final jr $31 will just bring it back to the beginning.
Here's my version:
.align 2
.text
daddui $sp,$0,0x400
main:
daddui $sp,$sp,-24
sw $fp,20($sp)
daddui $fp,$sp,0
sw $0,8($fp)
j $L2
nop
$L3:
lw $2,8($fp)
daddui $2,$2,1
sw $2,8($fp)
$L2:
lw $2,8($fp)
slti $2,$2,10
bne $2,$0,$L3
nop
daddui $2,$0,0
daddui $sp,$fp,0
lw $fp,20($sp)
daddui $sp,$sp,24
jr $31
nop
Consider getting either another compiler or another simulator, because these two clearly hate each other.
Related
I have converted a c code into mips32 using gcc compiler but there are some parts that I did not understand.
This is c code:
int main()
{
float fibSquared;
int F[10] = {0};
F[0] = 0;
F[1] = 1;
for(int i = 2; i < 10 ; i++)
F[i] = F[i-1] + F[i-2];
fibSquared = sqrt(F[9]);
printf("%f",fibSquared);
return 0;
}
This is mips code:
.file 1 ""
.section .mdebug.abi32
.previous
.nan legacy
.module fp=32
.module nooddspreg
.abicalls
.rdata
.align 2
$LC0:
.ascii "%f\000"
.text
.align 2
.globl main
.set nomips16
.set nomicromips
.ent main
.type main, #function
main:
.frame $fp,80,$31 # vars= 48, regs= 2/0, args= 16, gp= 8
.mask 0xc0000000,-4
.fmask 0x00000000,0
.set noreorder
.cpload $25
.set nomacro
addiu $sp,$sp,-80
sw $31,76($sp)
sw $fp,72($sp)
move $fp,$sp
.cprestore 16
movz $31,$31,$0
sw $0,32($fp)
sw $0,36($fp)
sw $0,40($fp)
sw $0,44($fp)
sw $0,48($fp)
sw $0,52($fp)
sw $0,56($fp)
sw $0,60($fp)
sw $0,64($fp)
sw $0,68($fp)
sw $0,32($fp)
li $2,1 # 0x1
sw $2,36($fp)
li $2,2 # 0x2
sw $2,24($fp)
b $L2
nop
$L3:
lw $2,24($fp)
nop
addiu $2,$2,-1
sll $2,$2,2
addiu $3,$fp,24
addu $2,$3,$2
lw $3,8($2)
lw $2,24($fp)
nop
addiu $2,$2,-2
sll $2,$2,2
addiu $4,$fp,24
addu $2,$4,$2
lw $2,8($2)
nop
addu $3,$3,$2
lw $2,24($fp)
nop
sll $2,$2,2
addiu $4,$fp,24
addu $2,$4,$2
sw $3,8($2)
lw $2,24($fp)
nop
addiu $2,$2,1
sw $2,24($fp)
$L2:
lw $2,24($fp)
nop
slt $2,$2,10
bne $2,$0,$L3
nop
lw $2,68($fp)
nop
mtc1 $2,$f0
nop
cvt.d.w $f0,$f0
mov.d $f12,$f0
lw $2,%call16(sqrt)($28)
nop
move $25,$2
.reloc 1f,R_MIPS_JALR,sqrt
1: jalr $25
nop
lw $28,16($fp)
cvt.s.d $f0,$f0
swc1 $f0,28($fp)
lwc1 $f0,28($fp)
nop
cvt.d.s $f0,$f0
mfc1 $7,$f0
mfc1 $6,$f1
lw $2,%got($LC0)($28)
nop
addiu $4,$2,%lo($LC0)
lw $2,%call16(printf)($28)
nop
move $25,$2
.reloc 1f,R_MIPS_JALR,printf
1: jalr $25
nop
lw $28,16($fp)
move $2,$0
move $sp,$fp
lw $31,76($sp)
lw $fp,72($sp)
addiu $sp,$sp,80
j $31
nop
.set macro
.set reorder
.end main
.size main, .-main
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609"
This is the part I could not understand:
lw $2,68($fp)
nop
mtc1 $2,$f0
nop
cvt.d.w $f0,$f0
mov.d $f12,$f0
lw $2,%call16(sqrt)($28)
nop
move $25,$2
.reloc 1f,R_MIPS_JALR,sqrt
1: jalr $25
nop
lw $28,16($fp)
cvt.s.d $f0,$f0
swc1 $f0,28($fp)
lwc1 $f0,28($fp)
nop
cvt.d.s $f0,$f0
mfc1 $7,$f0
mfc1 $6,$f1
lw $2,%got($LC0)($28)
nop
addiu $4,$2,%lo($LC0)
lw $2,%call16(printf)($28)
nop
move $25,$2
.reloc 1f,R_MIPS_JALR,printf
1: jalr $25
nop
lw $28,16($fp)
move $2,$0
move $sp,$fp
lw $31,76($sp)
lw $fp,72($sp)
addiu $sp,$sp,80
j $31
nop
.set macro
.set reorder
.end main
.size main, .-main
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609"
What does .reloc mean?
How does %call16 work?
What is the corresponding value of the variable fibSquared?
How the functions "printf" and "sqrt" are called and executed?
I think you take the same class I do because I have the same homework, so I am going to share what I understood myself, it may help.
For %call16, if you noticed between the brackets, the name that the C code uses from the included files such as printf from stdio.h, sqrt from math file, so it's clearly calling them so he can use them.
About the .reloc, I have noticed that they come after %call16 and the called function executed and is for relocating the memory, so I think it reorders the included files from any damage that may have happened.
The fibSquared value is located in mem[-12] (SW $3,8($2))$2=-20, then it's moved to $f0 by(mtc1 $2,$f0). In the instruction, the second par is the destination($f0). It's converted from int to float in(cvt.d.w $f0,$f0).
The sqrt executed in this part of the code:
lw $2,%call16(sqrt)($28) \\ in this particular
nop
move $25,$2
.reloc 1f,R_MIPS_JALR,sqrt
1: jalr $25
$25 at the beginning was chosen for cpload (.cpload $25)
** printf :
lw $2,%got($LC0)($28)
nop
addiu $4,$2,%lo($LC0)
lw $2,%call16(printf)($28)\\ in this particular
nop
move $25,$2
.reloc 1f,R_MIPS_JALR,printf
1: jalr $25
I assemble a simple c program to mips and try to understand the assembly code. By comparing with c code, I almost understand the it but still get some problems.
I use mips-gcc to generate assembly code: $ mips-gcc -S -O2 -fno-delayed-branch -I/usr/include lab3_ex3.c -o lab3_ex3.s
Here is my guess about how the assembly code works:
main is the entry of the program.
$6 is the address of source array.
$7 is the address of dest array.
$3 is the size of source array.
$2 is the variable k and is initialized to 0.
$L3 is the loop
$5 and $4 are addresses of source[k] and dest[k].
sw $3,0($5) is equivalent to store source[k] in $3.
lw $3,4($4) is equivalent to assign source[k] to dest[k].
addiu $2,$2,4 is equivalent to k++.
bne $3, $0, $L3 means that if source[k] is zero then exits the loop otherwise jump to lable $L3.
$L2 just do some clean up work.
Set $2 to zero.
Jump to $31 (return address).
My problems is:
What .frame $sp,0,$31 does?
Why lw $3,4($4) instead of lw $3,0($4)
What is the notation%lo(source)($6) means? ($hi and $lo$ registers are used in multiply so why they are used here?)
Thanks.
C
int source[] = {3, 1, 4, 1, 5, 9, 0};
int dest[10];
int main ( ) {
int k;
for (k=0; source[k]!=0; k++) {
dest[k] = source[k];
}
return 0;
}
Assembly
.file 1 "lab3_ex3.c"
.section .mdebug.eabi32
.previous
.section .gcc_compiled_long32
.previous
.gnu_attribute 4, 1
.text
.align 2
.globl main
.set nomips16
.ent main
.type main, #function
main:
.frame $sp,0,$31 # vars= 0, regs= 0/0, args= 0, gp= 0
.mask 0x00000000,0
.fmask 0x00000000,0
lui $6,%hi(source)
lw $3,%lo(source)($6)
beq $3,$0,$L2
lui $7,%hi(dest)
addiu $7,$7,%lo(dest)
addiu $6,$6,%lo(source)
move $2,$0
$L3:
addu $5,$7,$2
addu $4,$6,$2
sw $3,0($5)
lw $3,4($4)
addiu $2,$2,4
bne $3,$0,$L3
$L2:
move $2,$0
j $31
.end main
.size main, .-main
.globl source
.data
.align 2
.type source, #object
.size source, 28
source:
.word 3
.word 1
.word 4
.word 1
.word 5
.word 9
.word 0
.comm dest,40,4
.ident "GCC: (GNU) 4.4.1"
Firstly, main, $L3 and $L2 are labels for 3 basic blocks. You are roughly correct about their functions.
Question 1: What is .frame doing
This is not a MIPS instruction. It is metadata describing the (stack) frame for this function:
The stack is pointed to by $sp, an alias for $29.
and the size of the stack frame (0, since the function has neither local variables, nor arguments on the stack). Further, the function is simple enough that it can work with scratch registers and does not need to save callee-saved registers $16-$23.
the old return address ($31 for MIPS calling convention)
For more information regarding the MIPS calling convention, see this doc.
Question 2: Why lw $3,4($4) instead of lw $3,0($4)
This is due to an optimization of the loop. Normally, the sequence of loads and stores would be :
load source[0]
store dest[0]
load source[1]
store dest[1]
....
You assume that the loop is entirely in $L3, and that contains load source[k] and store dest[k]. It isn't. There are two clues to see this:
There is a load in the block main which does not correspond to any load outside the loop
Within the basic block $L3, the store is before the load.
In fact, load source[0] is performed in the basic-block named main. Then, the loop in the basic block $L3 is store dest[k];load source[k+1];. Therefore, the load uses an offset of 4 more than the offset of the store, because it is loading the integer for the next iteration.
Question 3: What is the lo/hi syntax?
This has to do with instruction encodings and pointers. Let us assume a 32-bit architecture, i.e. a pointer is 32 bits. Like most fixed-size instruction ISAs, let us assume that the instruction size is also 32 bits.
Before loading and storing from the source/dest arrays, you need to load their pointers into registers $6 and $7 respectively. Therefore, you need an instruction to load a 32-bit constant address into a register. However, a 32-bit instruction must contain a few bits to encode opcodes (which operation the instruction is), destination register etc. Therefore, an instruction has less than 32 bits left to encode constants (called immediates). Therefore, you need two instructions to load a 32-bit constant into a register, each loading 16 bits. The lo/hi refer to which half of the constant is loaded.
Example: Assume that dest is at address 0xabcd1234. There are two instructions to load this value into $7.
lui $7,%hi(dest)
addiu $7,$7,%lo(dest)
lui is Load Upper immediate. It loads the top 16 bits of the address of dest (0xabcd) into the top 16 bits of $7. Now, the value of $7 is 0xabcd0000.
addiu is Add Immediate Unsigned. It adds the lower 16 bits of the address of dest (0x1234) with the existing value in $7 to get the new value of $7. Thus, $7 now holds 0xabcd0000 + 0x1234 = 0xabcd1234, the address of dest.
Similarly, lw $3,%lo(source)($6) loads from the address pointed to by $6 (which already holds the top 16 bits of the address of source) at an offset of %lo(source) (the bottom 16 bits of that address). Effectively, it loads the first word of source.
This is the C source code
#include <stdio.h>
int main() {
printf("The Factorial of 10 is %d\n", fact(10));
}
int fact(int n) {
if (n < 1)
return (1);
else
return (n * fact(n - 1));
}
I am converting a C Programming function to a MIPS, but when I run the MIPS program I am getting an error for the .ascii section.
.text
.globl main
main:
subu $sp,$sp,32 # Stack frame is 32 bytes long
sw $ra,20($sp) # Save return address
sw $fp,16($sp) # Save old frame pointer
addiu $fp,$sp,28 # Set up frame pointer
li $a0,10 # Put argument (10) in $a0
jal fact # Call factorial function
la $a0,$LC # Put format string in $a0
move $a1,$v0 # Move fact result to $a1
jal printf # Call the print function
lw $ra,20($sp) # Restore return address
lw $fp,16($sp) # Restore frame pointer
addiu $sp,$sp,32 # Pop stack frame
jr $ra # Return to caller
.rdata
$LC:
.ascii “The factorial of 10 is %d\n\000”
.text
fact:
subu $sp,$sp,32 # Stack frame is 32 bytes long
sw $ra,20($sp) # Save return address
sw $fp,16($sp) # Save frame pointer
addiu $fp,$sp,28 # Set up frame pointer
sw $a0,0($fp) # Save argument (n) to use for Recursive Call
lw $v0,0($fp) # Load n
bgtz $v0,$L2 # Branch if n > 0
li $v0,1 # Return 1
jr $L1 # Jump to code to return
$L2:
lw $v1,0($fp) # Load n
subu $v0,$v1,1 # Compute n - 1
move $a0,$v0 # Move value to $a0
jal fact # Call factorial function
lw $v1,0($fp) # Load n
mul $v0,$v0,$v1 # Compute fact(n-1) * n
$L1: # Result is in $v0
lw $ra, 20($sp) # Restore $ra
lw $fp, 16($sp) # Restore $fp
addiu $sp, $sp, 32 # Pop stack
jr $ra # Return to caller
It's giving me an error for the .ascii code section saying it shouldn't be in the .text:
Error in ".ascii" directive cannot appear in text segment
It's also saying that:
"$L1": operand is of incorrect type
It's giving me an error for the .ascii code section saying it shouldn't be in the .text:
Error in ".ascii" directive cannot appear in text segment"
I am going out on a limb here because I am not 100% sure what you are running this on, but some sims like MARS don't recognize the rdata segment. You can try using just .data.
Also, if you are on something like WinMIPS64, you may want to try placing the .data segment at the top of the code. I understand what you are doing is right in some environments and but doesn't work in others, so give it a whirl.
May I suggest you try these things separately, just in case.
I want to write code that has a public C interface, but is otherwise implemented entirely in MIPS, mainly as a learning exercise. However, I'm stuck fighting GAS because it seems to assume it knows better than I do.
To illustrate, let's say I want to implement the following in MIPS:
int bar(void)
{
return (4 / 2);
}
As I mentioned above, I want to be able to call ASM routines from C, so we'll need a C code file, bar.c, as well as the MIPS assembly in bar.S.
bar.c:
extern int bar(void);
void _start()
{
int foo = bar();
}
bar.S:
.global bar .text
bar:
addi $2, $0, 4
addi $3, $0, 2
div $2, $3
mflo $2
jr $31
addu $3, $0, $0
mipsel-none-elf-gcc bar.c bar.S -o bar.elf -ffreestanding -nostdinc -nostdlib successfully compiles this, but mipsel-none-elf-objdump -d bar.elf shows that GAS is messing with my bar() code:
00400050 <bar>:
400050: 20020004 addi v0,zero,4
400054: 20030002 addi v1,zero,2
400058: 14600002 bnez v1,400064 <bar+0x14>
40005c: 0043001a div zero,v0,v1
400060: 0007000d break 0x7
400064: 2401ffff li at,-1
400068: 14610004 bne v1,at,40007c <bar+0x2c>
40006c: 3c018000 lui at,0x8000
400070: 14410002 bne v0,at,40007c <bar+0x2c>
400074: 00000000 nop
400078: 0006000d break 0x6
40007c: 00001012 mflo v0
400080: 00001012 mflo v0
400084: 03e00008 jr ra
400088: 00000000 nop
40008c: 00001821 move v1,zero
I don't want the division checks, or delay slot fixups, or the assembler being "helpful" in any way here: I'm perfectly capable of handling those things myself if necessary. How do I tell GAS to just be a dumb assembler?
This one was solved in the comments. GAS accepts a 3-operand version of div where division checks are disabled if the first operand is $0. As for the delay slot, a .set noreorder directive prevents reordering of instructions (duh). Changing the assembly to:
.set noreorder
.global bar .text
bar:
addi $2, $0, 4
addi $3, $0, 2
div $0, $2, $3
mflo $2
jr $31
addu $3, $0, $0
produces the correct output:
00400050 <bar>:
400050: 20020004 addi v0,zero,4
400054: 20030002 addi v1,zero,2
400058: 0043001a div zero,v0,v1
40005c: 00001012 mflo v0
400060: 03e00008 jr ra
400064: 00001821 move v1,zero
I have another homework question that, like the last question I asked on this site, wasn't explained well by the teacher nor the textbook. Here's the question:
Translate this C statement into MIPS assembly instructions:
B[8] = A[i-j];
Assume variables f, g, h, i and j and are assigned to registers $s0, $s1, $s2, $s3, and $s4, respectively. Assume the base addresses of the arrays A and B are in registers $s6 and $s7, respectively.
Now, where I'm stuck is adding the two variables and using the result as an offset. So far, I have the following:
sub $t0, $s3, $s4 # add values to get offset amount, store in $t0
sll $t1, $t0,2 # multiply the offset by 4, store in $t1
Now, I don't know if I can use $t1 as an offset to access that array element. It looks like the textbook only uses numbers (e.g. 4($s7)) instead of registers (e.g. $t1($s7)) What do I do next?
A compiler can only translate complete program, so here's a complete program that includes your instruction, with its translating into MIPS asembly instrution. I hope you can study what's being done here and draw some conclusions.
int main() {
int i = 3;
int j = 2;
int B[3] = {10, 20, 30};
int A[3] = {100, 200, 300};
B[8] = A[i-j];
}
MIPS for the above:
.file 1 "Cprogram.c"
# -G value = 8, Cpu = 3000, ISA = 1
# GNU C version cygnus-2.7.2-970404 (mips-mips-ecoff) compiled by GNU C version cygnus-2.7.2-970404.
# options passed: -msoft-float
# options enabled: -fpeephole -ffunction-cse -fkeep-static-consts
# -fpcc-struct-return -fcommon -fverbose-asm -fgnu-linker -msoft-float
# -meb -mcpu=3000
gcc2_compiled.:
__gnu_compiled_c:
.rdata
.align 2
$LC0:
.word 10
.word 20
.word 30
.align 2
$LC1:
.word 100
.word 200
.word 300
.text
.align 2
.globl main
.ent main
main:
.frame $fp,64,$31 # vars= 40, regs= 2/0, args= 16, extra= 0
.mask 0xc0000000,-4
.fmask 0x00000000,0
subu $sp,$sp,64
sw $31,60($sp)
sw $fp,56($sp)
move $fp,$sp
jal __main
li $2,3 # 0x00000003
sw $2,16($fp)
li $2,2 # 0x00000002
sw $2,20($fp)
addu $2,$fp,24
la $3,$LC0
lw $4,0($3)
lw $5,4($3)
lw $6,8($3)
sw $4,0($2)
sw $5,4($2)
sw $6,8($2)
addu $2,$fp,40
la $3,$LC1
lw $4,0($3)
lw $5,4($3)
lw $6,8($3)
sw $4,0($2)
sw $5,4($2)
sw $6,8($2)
lw $2,16($fp)
lw $3,20($fp)
subu $2,$2,$3
move $3,$2
sll $2,$3,2
addu $3,$fp,16
addu $2,$2,$3
addu $3,$2,24
lw $2,0($3)
sw $2,56($fp)
$L1:
move $sp,$fp # sp not trusted here
lw $31,60($sp)
lw $fp,56($sp)
addu $sp,$sp,64
j $31
.end main