CMPUT 229 - Computer Organization and Architecture I

If/Elif/Else Conditional

229 RISC-V Examples - If/Elif/Else Conditional

Examples of Code Patterns and Structure in RISC-V - Arguments and Return Values

Arguments and Return Values

This page covers how to pass arguments to a function and return values from one. RISC-V has eight argument registers: a0 through a7. Arguments are passed to a function through these registers. Return values produced by a function are passed back in registers a0 and a1.

Passing Arguments

Consider a function foo that calls a subroutine foil which when given number a in register a0 and number b in register a1 calculates a^2 + 2*a*b + b^2 and prints the resulting value to stdout. (Foils out (a + b)^2) Here is one possible implementation of foo and foil:

#-------------------------------------------------------------------------------
# foo
# A function that does something, and as a sub-task foils two numbers.
#
# Arguments:
#       None
# Return Values:
#       None
# Side Effects:
#       None
# Register Usage:
#       t0, t1: Intermediaries to be foiled out.
#
#-------------------------------------------------------------------------------
foo:
    # Imagine we are doing some task which results in t0, t1 having
    # the values a, b which we want to foil out.

    # Move a, b to a0, a1.
    add     a0, zero, t0    # a0 <- a
    add     a1, zero, t1    # a1 <- b

    jal     ra, foil        # foil(a, b)

    # Lastly, return from foo.
    jalr    zero, ra, 0

#-------------------------------------------------------------------------------
# foil
# Given two numbers a and b, this function foils them out.
# That is, expands (a + b)^2 into a^2 + 2ab + b^2.
#
# Arguments:
#       a0: The value of a.
#       a1: The value of b.
# Return Values:
#       None
# Side Effects:
#       Prints the sum of a^2 + 2ab + b^2 to terminal.
# Register Usage:
#       t0, t1: Intermediaries in calculations.
#
#-------------------------------------------------------------------------------
foil:
    # Calculate the squares.
    mul     t0, a0, a0      # t0 <- a^2
    mul     t1, a1, a1      # t1 <- b^2
    add     t0, zero, t1    # t0 <- a^2 + b^2

    # Now the 2 * a * b term.
    mul     t1, a0, a1      # t1 <- a * b
    add     t0, t0, t1      # t0 <- a^2 + b^2 + a*b
    add     t0, t0, t1      # t0 <- a^2 + b^2 + 2*a*b

    # To print it out, we need to move the value into a0 and the number 1 into a7.
    mv      a0, t0
    addi    a7, zero, 1
    ecall

    # Now we return to foo.
    jalr    zero, ra, 0

The order of the arguments, or what the arguments are needed, is up to the programmer implementing the function. If a function is created from a specification, the specification must precisely indicate which arguments the function expects, which values the function returns, and which side effects the function has. The function header should contain this information.

Return Values

A function can return values in the argument registers a0 and a1. Before returning, a function must ensure that the expected return values are in the registers a0 or a1. Whenever possible, it is a good practice to already use these registers for the computation of the values that will be returned. This practice eliminates the need for move instructions prior to returning.

Consider the code example from before. In this variation the funtion foo returns the calculated value to instead of printing it:

foil:

    # Calculating a^2 + 2*a*b + b^2 is the same.

    # Move value to a0 and return.
    mv      a0, t0
    jalr    zero, ra, 0

Link to a file containing all of the above code snippets