Regarding the $gp syntax, I find it a little bit confusing, if $gp points to some kind of address, then offset this with 'const5' (correct me if i am wrong here cause I am not sure), then what would happen? also, const5 isn't a floating point number either, so I am wondering how does this lwc1 as well as const5($gp) work behind the scene.
The exercise is to translate the below C-instructions into MIPS-Assembly Language:
float f2c (float fahr)
{
return ((5.0/9.0)*(fahr - 32.0));
}
The translated MIPS (from the textbook):
f2c:
lwc1 $f16, const5($gp);
lwc1 $f18, const9($gp);
...
CodePudding user response:
lwc1
is a load instruction that operates just like lw
but loads into a floating point register instead of an integer register. lwc1
does not examine, interpret, or verify the value being loaded, it simply loads a 4-byte aka 32-bit value at the effective address in memory into the named floating point register without modification.
lwc1
can be used to load an integer or a floating point value into the floating point register. After loading an integer, you might use cvt.s.w
to convert an integer in a floating point register to floating point value (in the same or another floating point register), so you can then do floating point add/sub etc..
Like lw
and sw
, the integer loads & stores on MIPS, the floating point load and store instructions have only one addressing mode. That addressing mode is base offset. The effective address is simply the contents of the base register the sign extended immediate/offset. Though lwc1
loads into a floating point register, the addressing mode still takes an integer register for the base.
$gp
is a register called the global (data) pointer. When setup properly by the program startup environment, it points 32k from the start of the global data section loaded into memory. This allows access to 64k of global data, by using both negative and positive values for the immediate. $gp
should not be modified by the program beyond startup, so that it can be relied upon to remain pointing to the global data section.
If the program is compiled to use $gp
, then there is a risk that the program has more global data than 64k, in which case you should get a linker error. $gp
allows the machine code to be independent of the actual location of the data, and also access sequence for global data is only one instruction in length. (The 64k limit applies only to global data, variables & constants, but not to dynamically allocated heap data.)
The alternative to using $gp
for accessing global data, such as when the environment doesn't support it, or a program has more than 64k of global data, is to use absolute addressing, which on MIPS takes 2 instructions, the first of which is lui
and the 2nd varies between addiu
, lw
or sw
.
In order for that lwc1 const5($gp)
to work,
- the program's object code (compiler output) needs to specify some floating point constants as global data perhaps with labels like __const5 & __const9,
- the programs object code needs to specify appropriate relocations on access to these labels,
- the linker needs to gather the global data and gives a value to names like const5 & const9 relative to the global data section 32k, and
- the startup code needs to put the proper value into
$gp
.
(The exact mechanism by which the constant 5 is given a label and later referenced goes to details of relocations specified in the object code, and likely differ somewhat from this overly simplified illustration.)
RISC V has similar instructions, lui
(plus another auipc
) and the various lw
s. It generally uses a 2 instruction sequence to access global data, but using a mechanism called relaxation the linker can perform link time optimizations reducing the code sequences from 2 instructions to 1 instruction whenever the access would be within reach. This relaxation mechanism solves the problem of programs having more than 64k of data, while still allowing 1 instruction accesses whenever possible. It is rather complex, since the linker will actually be changing the size of code sections (i.e. deleting code from the middle of some object code), meaning the compiler has to generate relocations for internal branches as well now.
Linker relaxation can theoretically be applied to any instruction set architecture that has shorter ways to express the same global accesses.