Printing the value
The primary problem we try to solve here is not printing an integer value but loading a value into the register. From our intro article we already know how to print/display a string using sparc assembly language program. We used the format string with
printf which included
%s. Moreover, from our article on Parameter Passing we know how to pass parameters and get return values from subroutines.
s0: .asciz "foo %s\n"
This allowed us printing a string. Now, to print a 64 bit value we will use as format specifier
s0: .asciz "foo %lld\n"
As we recall, second argument of
printf (for the string printing program) was passed using register
%O1 and the first argument was passed using
%O0. Therefore to print an integer by passing it to printf using a format specifier string we need to pass this using the second register
%O1 just like before. So we need to figure out how to put/load that value into register
Loading a value into register - Intro
To load a value into a register sparc does not have native
mov instructions like x86. Instead we use the
add (check sparc v9 doc) instruction. From the article, SPARC Registers we know that the global variable G0 is hardwired zero. Therefore an instruction like this easily loads a value into the register,
or %g0, 1, %l1
or instruction does an
or between 0 and 1 and loads into first local register. An
add instruction would serve the same purpose.
add %g0, 1, %l1
Loading 64 bit value into the register
We load the 64 bit value in 3 steps. First, we load a 32 bit value (higher 32 bit parts of the value) into the register. Then, shift this value 32 bits (lower 32 bit parts of the value). Afterwards, we load 32 bit value. Finally, we add those two which gives us the final 64 bit value into the register.
Loading a 32 bit value into register
Let's say, we want to load
4097 = 2^12+1 into the register.
- 1 can be loaded into the register using a simple
2^12can be loaded into the register using the
As we know,
sethi instruction sets bits starting from 11th bit upto 32nd bit. The number 2^12 has 13th bit set to 1 and others to 0. Therefore, to make it
2^12 we need to set
13-10 = 3 3rd bit starting from 11tbit.
- first bit - 11th bit
- second bit - 12th bit
- third bit - 13th bit
The binary representation for this value is: 0b0100. Hex representation is: 0x4 Following two instructions enable us to load the value 4097 into a register,
sethi 0x4, %o1 or %o1, 0x1, %o1
If you try our example program with above two instructions we will get,
$ ./p64_07_load_32bit_value Loaded value 4097
On powershell we get,
The values match.
Shifting Higher Part
Now let's say we want to load the same value but on the higher 32 bit part of the register. For the same value, 4097 on the higher part does this: it sets 44th bit of the 64bit register to 1 and 32nd register to 1. Which is equivalent to,
2^44 + 2^32 = 17596481011712
We need to shift the loaded value 32 bits to make room for the lower 32 bit part. We use
sllx instruction to do the shifting.
sethi 0x4, %o1 or %o1, 0x1, %o1 sllx %o1, 32, %o1
This does following: it sets 44th bit of the 64bit register to 1 and 32nd register to 1.
If you try our example program with above three instructions we get,
$ ./p64_08_load_64bit_shift Loaded value 17596481011712
To verify, we run following on powershell,
$ [math]::pow(2,44)+[math]::pow(2,32) 17596481011712
Which validates that our program is correct so far.
Loading HI 32 bit and LO 32 bit (actually 64 bit)
If we are able to load the HI part of the value on higher part of the register and LO part of the value into lower part of the register: say for easy illustration hi 32 bits and low 32 bits we are done. In previous section we have seen demonstration of loading HI part. Can we just add the instructions for loading the LO part?
What about the example instructions below,
sethi 0x4, %o1 or %o1, 0x1, %o1 sllx %o1, 32, %o1 sethi 0x4, %o1 or %o1, 0x1, %o1
Do you think this would work? No. Becacuse the second sethi will set the left 32 bits to 0. As a result, we will be lefft with the value 4097 and HI part will be set to 0. For this reason, this is what we see,
$ ./p64_08_load_64bit_value_fail Loaded value 4097
Therefore, we are gonna need another temporary register to hold the value before
sethi sets the high part to 0. We are gonna use the first local register
%l0 as the temporary register. Then the implementation looks like following,
sethi 0x4, %l0 or %l0, 0x1, %l0 sllx %l0, 32, %l0 sethi 0x4, %o1 or %o1, 0x1, %o1 or %l0, %o1, %o1
Entire example program is illustrated below,
.data s0: .asciz "Loaded value %lld\n" .text .global main .align 4 .type main, #function main: save %sp, -0xc0, %sp call print_large_val nop ret restore print_large_val: save %sp, -0xc0, %sp setx s0, %l4, %o0 ! for illustration (not complicating) we are not replacing setx sethi 0x4, %l0 or %l0, 0x1, %l0 sllx %l0, 32, %l0 sethi 0x4, %o1 or %o1, 0x1, %o1 or %l0, %o1, %o1 call printf nop ret restore
Compiling and running the output binary we see,
$ suncc -m64 -s p64_08_load_64bit_value.S -o p64_08_load_64bit_value $ ./p64_08_load_64bit_value Loaded value 17596481015809
On powershell, we verify this,
$ [math]::pow(2,44)+[math]::pow(2,32)+[math]::pow(2,12)+1 17596481015809
Finally, if we look at the synthetic
setx instruction we will see it is performing exactly the same thing.
- Loading the HI part of the value into the register
- shifting that 32 bits to make room for low HI bits
- Loading the LO 32 bits into the register
- Finally, adding them and put into the destination register
- Logical Operators: AND, NAND, OR, NOR, XOR, XNOR - Section A.31 page# 208
- sparc synthetic mov and setex instruction - sparc v9 ref table#37, page# 321
- SETHI sparc v9 ref - Section# A.48 page# 248
- A.49 shift instructions including slx page# 248
- Section 8.5.1 - LOading 32-bit Constants of SPARC Architecture, Assembly Language Programming, and C (2nd Edition)