I am a master student and currently doing my summer final project, which is about design a MIPS processor with a FPU and implement in a FPGA.
The instructions that I'm going to implement are depended on the cross-compiler I'm using. So, from a hardware designer point of view, I started the project by first looking the instructions can be generated from the compiler.
For integer design(the main core design), I wrote some C codes, here for example, a simple one:
int main ()
{
int a,b,c;
a=1;
b=2;
c=a+2;
}
A simple addition, the compiler gives assembly codes: (I just posted the assembly codes in main, because I did not plan to run a operating system on my MIPS)
00400168 <main>:
400168: 27bdffe8 addiu sp,sp,-24
40016c: afbe0010 sw s8,16(sp)
400170: 03a0f021 move s8,sp
400174: 24020001 li v0,1
400178: afc20008 sw v0,8(s8)
40017c: 24020002 li v0,2
400180: afc20004 sw v0,4(s8)
400184: 8fc20008 lw v0,8(s8)
400188: 00000000 nop
40018c: 20420002 addi v0,v0,2
400190: afc20000 sw v0,0(s8)
400194: 03c0e821 move sp,s8
400198: 8fbe0010 lw s8,16(sp)
40019c: 27bd0018 addiu sp,sp,24
4001a0: 03e00008 jr ra
I like to understand the assembly code, that can helps me more understand MIPS architecture, and based on the instructions order I can design a hazard detection unit based on the compiler.
We can see from those 4 instruction:
400174: 24020001 li v0,1
400178: afc20008 sw v0,8(s8)
40017c: 24020002 li v0,2
400180: afc20004 sw v0,4(s8)
The compiler loads 1, 2 into variable a, b. For the integer assembly code, I can understand no problems.
Ok, lets go to the floating point unit, as the same, I wrote a very similar C :
Floating point testing C code
void main ()
{
float a,b,c;
a=1;
b=2;
c=a+b;
}
Now the assembly codes are much different:
00400168 <main>:
400168: 27bdffe8 addiu sp,sp,-24
40016c: afbe0010 sw s8,16(sp)
400170: 03a0f021 move s8,sp
400174: c7808004 lwc1 $f0,-32764(gp)
400178: 00000000 nop
40017c: e7c00008 swc1 $f0,8(s8)
400180: c7808008 lwc1 $f0,-32760(gp)
400184: 00000000 nop
400188: e7c00004 swc1 $f0,4(s8)
40018c: c7c20008 lwc1 $f2,8(s8)
400190: c7c00004 lwc1 $f0,4(s8)
400194: 00000000 nop
400198: 46001000 add.s $f0,$f2,$f0
40019c: e7c00000 swc1 $f0,0(s8)
4001a0: 03c0e821 move sp,s8
4001a4: 8fbe0010 lw s8,16(sp)
4001a8: 27bd0018 addiu sp,sp,24
4001ac: 03e00008 jr ra
4001b0: 00000000 nop
Doesn't like pervious code, those 6 instructions looks like the program load the variable's value from data memory instead using instruction li:
400174: c7808004 lwc1 $f0,-32764(gp)
400178: 00000000 nop
40017c: e7c00008 swc1 $f0,8(s8)
400180: c7808008 lwc1 $f0,-32760(gp)
400184: 00000000 nop
400188: e7c00004 swc1 $f0,4(s8)
Here comes the problem, I just can not figure out what is value stored in -32764(gp) and f0,-32760(gp), because there are not any SW instructions that try to store data into those address.
Here is the fully assembly code generated by compiler:
floatadd: file format elf32-bigmips
Disassembly of section .init:
00400018 <_init>:
400018: 27bdffe0 addiu sp,sp,-32
40001c: afbf0014 sw ra,20(sp)
400020: 0c10003a jal 4000e8 <frame_dummy>
400024: 00000000 nop
400028: 0c10006d jal 4001b4 <__do_global_ctors_aux>
40002c: 00000000 nop
400030: 8fbf0014 lw ra,20(sp)
400034: 27bd0020 addiu sp,sp,32
400038: 03e00008 jr ra
40003c: 00000000 nop
Disassembly of section .text:
00400040 <_ftext>:
400040: 27bdffe0 addiu sp,sp,-32
400044: afb10014 sw s1,20(sp)
400048: 3c110040 lui s1,0x40
40004c: 9222126c lbu v0,4716(s1)
400050: afbf0018 sw ra,24(sp)
400054: 14400019 bnez v0,4000bc <_ftext+0x7c>
400058: afb00010 sw s0,16(sp)
40005c: 3c100040 lui s0,0x40
400060: 8e021260 lw v0,4704(s0)
400064: 00000000 nop
400068: 8c430000 lw v1,0(v0)
40006c: 00000000 nop
400070: 10600009 beqz v1,400098 <_ftext+0x58>
400074: 24420004 addiu v0,v0,4
400078: 0060f809 jalr v1
40007c: ae021260 sw v0,4704(s0)
400080: 8e021260 lw v0,4704(s0)
400084: 00000000 nop
400088: 8c430000 lw v1,0(v0)
40008c: 00000000 nop
400090: 1460fff9 bnez v1,400078 <_ftext+0x38>
400094: 24420004 addiu v0,v0,4
400098: 3c020000 lui v0,0x0
40009c: 24420000 addiu v0,v0,0
4000a0: 10400005 beqz v0,4000b8 <_ftext+0x78>
4000a4: 24020001 li v0,1
4000a8: 3c040040 lui a0,0x40
4000ac: 0c000000 jal 0 <_init-0x400018>
4000b0: 24840244 addiu a0,a0,580
4000b4: 24020001 li v0,1
4000b8: a222126c sb v0,4716(s1)
4000bc: 8fbf0018 lw ra,24(sp)
4000c0: 8fb10014 lw s1,20(sp)
4000c4: 8fb00010 lw s0,16(sp)
4000c8: 03e00008 jr ra
4000cc: 27bd0020 addiu sp,sp,32
004000d0 <call___do_global_dtors_aux>:
4000d0: 27bdffe8 addiu sp,sp,-24
4000d4: afbf0010 sw ra,16(sp)
4000d8: 8fbf0010 lw ra,16(sp)
4000dc: 00000000 nop
4000e0: 03e00008 jr ra
4000e4: 27bd0018 addiu sp,sp,24
004000e8 <frame_dummy>:
4000e8: 3c020000 lui v0,0x0
4000ec: 27bdffe8 addiu sp,sp,-24
4000f0: 3c040040 lui a0,0x40
4000f4: 3c050040 lui a1,0x40
4000f8: 24420000 addiu v0,v0,0
4000fc: afbf0010 sw ra,16(sp)
400100: 24840244 addiu a0,a0,580
400104: 10400003 beqz v0,400114 <frame_dummy+0x2c>
400108: 24a51270 addiu a1,a1,4720
40010c: 0c000000 jal 0 <_init-0x400018>
400110: 00000000 nop
400114: 3c040040 lui a0,0x40
400118: 8c831258 lw v1,4696(a0)
40011c: 3c020000 lui v0,0x0
400120: 10600007 beqz v1,400140 <frame_dummy+0x58>
400124: 24590000 addiu t9,v0,0
400128: 24841258 addiu a0,a0,4696
40012c: 13200004 beqz t9,400140 <frame_dummy+0x58>
400130: 00000000 nop
400134: 8fbf0010 lw ra,16(sp)
400138: 03200008 jr t9
40013c: 27bd0018 addiu sp,sp,24
400140: 8fbf0010 lw ra,16(sp)
400144: 00000000 nop
400148: 03e00008 jr ra
40014c: 27bd0018 addiu sp,sp,24
00400150 <call_frame_dummy>:
400150: 27bdffe8 addiu sp,sp,-24
400154: afbf0010 sw ra,16(sp)
400158: 8fbf0010 lw ra,16(sp)
40015c: 00000000 nop
400160: 03e00008 jr ra
400164: 27bd0018 addiu sp,sp,24
00400168 <main>:
400168: 27bdffe8 addiu sp,sp,-24
40016c: afbe0010 sw s8,16(sp)
400170: 03a0f021 move s8,sp
400174: c7808004 lwc1 $f0,-32764(gp)
400178: 00000000 nop
40017c: e7c00008 swc1 $f0,8(s8)
400180: c7808008 lwc1 $f0,-32760(gp)
400184: 00000000 nop
400188: e7c00004 swc1 $f0,4(s8)
40018c: c7c20008 lwc1 $f2,8(s8)
400190: c7c00004 lwc1 $f0,4(s8)
400194: 00000000 nop
400198: 46001000 add.s $f0,$f2,$f0
40019c: e7c00000 swc1 $f0,0(s8)
4001a0: 03c0e821 move sp,s8
4001a4: 8fbe0010 lw s8,16(sp)
4001a8: 27bd0018 addiu sp,sp,24
4001ac: 03e00008 jr ra
4001b0: 00000000 nop
004001b4 <__do_global_ctors_aux>:
4001b4: 3c020040 lui v0,0x40
4001b8: 2442124c addiu v0,v0,4684
4001bc: 8c44fffc lw a0,-4(v0)
4001c0: 27bdffe0 addiu sp,sp,-32
4001c4: 2403ffff li v1,-1
4001c8: afb00010 sw s0,16(sp)
4001cc: afbf0018 sw ra,24(sp)
4001d0: afb10014 sw s1,20(sp)
4001d4: 10830008 beq a0,v1,4001f8 <__do_global_ctors_aux+0x44>
4001d8: 2450fffc addiu s0,v0,-4
4001dc: 2411ffff li s1,-1
4001e0: 0080f809 jalr a0
4001e4: 2610fffc addiu s0,s0,-4
4001e8: 8e040000 lw a0,0(s0)
4001ec: 00000000 nop
4001f0: 1491fffb bne a0,s1,4001e0 <__do_global_ctors_aux+0x2c>
4001f4: 00000000 nop
4001f8: 8fbf0018 lw ra,24(sp)
4001fc: 8fb10014 lw s1,20(sp)
400200: 8fb00010 lw s0,16(sp)
400204: 03e00008 jr ra
400208: 27bd0020 addiu sp,sp,32
0040020c <call___do_global_ctors_aux>:
40020c: 27bdffe8 addiu sp,sp,-24
400210: afbf0010 sw ra,16(sp)
400214: 8fbf0010 lw ra,16(sp)
400218: 00000000 nop
40021c: 03e00008 jr ra
400220: 27bd0018 addiu sp,sp,24
Disassembly of section .fini:
00400224 <_fini>:
400224: 27bdffe0 addiu sp,sp,-32
400228: afbf0014 sw ra,20(sp)
40022c: 0c100010 jal 400040 <_ftext>
400230: 00000000 nop
400234: 8fbf0014 lw ra,20(sp)
400238: 27bd0020 addiu sp,sp,32
40023c: 03e00008 jr ra
400240: 00000000 nop
I am not good at MIPS assembly, can someone explain where are the floating point variable' value 1 and 2?
ELF executables can have one or more sections filled with the static data (string, floating point numbers, numbers, whatever) used by the program.
This sections are loaded into memory by the loader with the rest of the program, thereby avoiding intermixing code and data and reducing the code size.
For the ELF on MIPS systems you should refer to this where there is this nice picture:
As you can see $gp
is used to address the section .sdata and .sbss, where the initial s stands for small.
All these efforts are taken to minimize code size as by using $gp
the compiler can generate 16 bit offsets (versus the 32 bit ones normally used).
Since the offset is signed, $gp
is placed in the middle of a (at most) 64 KiB region formed by .sdata + .sbss.
Your floating points value are not coded directly in the instructions because FP instruction does not takes immediates, instead they are saved into a readonly section and loaded from there.
Why in the end do you care about this?
If your goal is to design an implementation of the MIPS ISA, just pick the specific ISA (MIPS32 I? MIPS32 IV? MIPS 64?), get the documents, get the whole picture and implement a microarchitecture for it.
If an instruction is a valid instruction according to your chosen ISA then your implementation must be able to execute it, don't worry about what the compilers are doing, they are grown up, they can take care of them selves and in the end if the code you are executing is broken, who cares? As long it is valid.
These will help you:
MIPS32™ Architecture For Programmers Volume I: Introduction to the MIPS32™ Architecture MIPS32™ Architecture For Programmers Volume II: The MIPS32™ Instruction Set