Martian148's blog Martian148's blog
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档

Martian148

一只热爱文科的理科生
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档
  • ACM - ICPC

  • 编程语言

  • 体系结构

    • 计算机组成原理笔记
    • CS61C

      • RISC-V速查表
      • CS61C Lab1
      • CS61C Lab2
      • CS61C Lab3
      • CSAPP

    • Web

    • 人工智能

    • 计算机网络

    • 数据库

    • 编程工具

    • 计算机科学
    • 体系结构
    • CS61C
    martian148
    2025-05-09
    目录

    CS61C Lab3

    # Lab 3

    # Exercise 1: Familiarizing yourself with Venus

    1. What do the .data, .word, .text directives mean (i.e. what do you use them for)? Hint: think about the 4 sections of memory.

    这里提示我们了内存的四个部分

    .data 表示内存数据上数据段的开始,

    .word 2, 4, 6, 8 表示在内存中连续存储4个32位整数(2、4、6、8)

    n: .word 9 定义了一个名为 n 的标签,指向一个值为9的32位整数,也就是 n=9

    .text表示代码段(text section)的开始。在这个段中,程序存放实际执行的机器指令(如函数、主程序等)。

    1. Run the program to completion. What number did the program output? What does this number represent?

    输出了 343434,这个数字是 斐波那契数列的第 999 个

    1. At what address is n stored in memory? Hint: Look at the contents of the registers.

    通过观察寄存器 x28 我们可以得到 n 的地址是 0x10000010

    1. Without actually editing the code (i.e. without going into the “Editor” tab), have the program calculate the 13th fib number (0-indexed) by manually modifying the value of a register. You may find it helpful to first step through the code. If you prefer to look at decimal values, change the “Display Settings” option at the bottom.

    答案是 233233233

    # Exercise 2: Translating from C to RISC-V

    • The register representing the variable k.
    t0
    
    1
    • The register representing the variable sum.
    s0
    
    1
    • The registers acting as pointers to the source and dest arrays.
    la s1, source
    la s2, dest
    
    1
    2
    • The assembly code for the loop found in the C code.
    for (k = 0; source[k] != 0; k++) {
        dest[k] = fun(source[k]);
        sum += dest[k];
    }
    
    1
    2
    3
    4
    • How the pointers are manipulated in the assembly code.

    在汇编代码中,先通过 la 命令获取数组的首地址,然后通过 add t1, s1, s3 来获取到 source[i] 的值

    # Exercise 3: Factorial

    在这个 Exercise 中,需要实现一个函数计算阶乘

    .globl factorial
    
    .data
    n: .word 10
    
    .text
    main:
        la t0, n
        lw a0, 0(t0)
        jal ra, factorial
    
        addi a1, a0, 0
        addi a0, x0, 1
        ecall # Print Result
    
        addi a1, x0, '\n'
        addi a0, x0, 11
        ecall # Print newline
    
        addi a0, x0, 10
        ecall # Exit
    
    factorial:
        # 需要实现一个函数计算阶乘
        addi t0, x0, 1    # t0 = 1
        addi t1, x0, 1    # t1 = 1
        
        addi t2, x0, 1
        beq a0, t2, end  # if a0 == t2 end
        beq a0, x0, end 
    
    loop:
        mul t0, t0, t1 # t0 = t0 * t1
        addi t1, t1, 1 # t1 += 1
        bne t1, a0, loop # if t1 <= n loop
    end:
        mv a0, t0 # a0 = t0
        ret
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38

    # Exercise 4: Calling Convention Checker

    这个练习需要你修复一个汇编代码,我们使用

    $ java -jar tools/venus.jar -cc lab03/cc_test.s
    
    1

    命令能获得当前代码的一些问题

    .globl simple_fn naive_pow inc_arr
    
    .data
    failure_message: .asciiz "Test failed for some reason.\n"
    success_message: .asciiz "Sanity checks passed! Make sure there are no CC violations.\n"
    array:
        .word 1 2 3 4 5
    exp_inc_array_result:
        .word 2 3 4 5 6
    
    .text
    main:
        # We test our program by loading a bunch of random values
        # into a few saved registers - if any of these are modified
        # after these functions return, then we know calling
        # convention was broken by one of these functions
        li s0, 2623
        li s1, 2910
        # ... skipping middle registers so the file isn't too long
        # If we wanted to be rigorous, we would add checks for
        # s2-s20 as well
        li s11, 134
        # Now, we call some functions
        # simple_fn: should return 1
        jal simple_fn # Shorthand for "jal ra, simple_fn"
        li t0, 1
        bne a0, t0, failure
        # naive_pow: should return 2 ** 7 = 128
        li a0, 2
        li a1, 7
        jal naive_pow
        li t0, 128
        bne a0, t0, failure
        # inc_arr: increments "array" in place
        la a0, array
        li a1, 5
        jal inc_arr
        jal check_arr # Verifies inc_arr and jumps to "failure" on failure
        # Check the values in the saved registers for sanity
        li t0, 2623
        li t1, 2910
        li t2, 134
        bne s0, t0, failure
        bne s1, t1, failure
        bne s11, t2, failure
        # If none of those branches were hit, print a message and exit normally
        li a0, 4
        la a1, success_message
        ecall
        li a0, 10
        ecall
    
    # Just a simple function. Returns 1.
    #
    # FIXME Fix the reported error in this function (you can delete lines
    # if necessary, as long as the function still returns 1 in a0).
    simple_fn:
        # mv a0, t0  
        li a0, 1
        ret
    
    # Computes a0 to the power of a1.
    # This is analogous to the following C pseudocode:
    #
    # uint32_t naive_pow(uint32_t a0, uint32_t a1) {
    #     uint32_t s0 = 1;
    #     while (a1 != 0) {
    #         s0 *= a0;
    #         a1 -= 1;
    #     }
    #     return s0;
    # }
    #
    # FIXME There's a CC error with this function!
    # The big all-caps comments should give you a hint about what's
    # missing. Another hint: what does the "s" in "s0" stand for?
    naive_pow:
        # BEGIN PROLOGUE
        # END PROLOGUE
    
        addi sp, sp, -8
        sw ra, 4(sp)
        sw s0, 0(sp)
        li s0, 1
    naive_pow_loop:
        beq a1, zero, naive_pow_end
        mul s0, s0, a0
        addi a1, a1, -1
        j naive_pow_loop
    naive_pow_end:
        mv a0, s0
        lw s0, 0(sp)
        lw ra, 4(sp)
        addi sp, sp, 8
        ret
    
    # Increments the elements of an array in-place.
    # a0 holds the address of the start of the array, and a1 holds
    # the number of elements it contains.
    #
    # This function calls the "helper_fn" function, which takes in an
    # address as argument and increments the 32-bit value stored there.
    inc_arr:
        addi sp, sp, -16
        sw ra, 12(sp)
        sw s0, 8(sp)
        sw s1, 4(sp)
    
        mv s0, a0 # Copy start of array to saved register
        mv s1, a1 # Copy length of array to saved register
        li t0, 0 # Initialize counter to 0
    inc_arr_loop:
        beq t0, s1, inc_arr_end
        slli t1, t0, 2 # Convert array index to byte offset
        add a0, s0, t1 # Add offset to start of exp_inc_array_result
    
        sw t0, 0(sp)
        jal helper_fn
        lw t0, 0(sp)
    
        addi t0, t0, 1 # Increment counter
        j inc_arr_loop
    inc_arr_end:
        lw s1, 4(sp)
        lw s0, 8(sp)
        lw ra, 12(sp)
        addi sp, sp, 16
        ret
    
    # This helper function adds 1 to the value at the memory address in a0.
    # It doesn't return anything.
    # C pseudocode for what it does: "*a0 = *a0 + 1"
    #
    # FIXME This function also violates calling convention, but it might not
    # be reported by the Venus CC checker (try and figure out why).
    # You should fix the bug anyway by filling in the prologue and epilogue
    # as appropriate.
    helper_fn:
        addi sp, sp, -8
        sw ra, 4(sp)
        sw s0, 0(sp)
    
        lw t1, 0(a0)
        addi s0, t1, 1
        sw s0, 0(a0)
        
        lw s0, 0(sp)
        lw ra, 4(sp)
        addi sp, sp, 8
        ret
    
    # YOU CAN IGNORE EVERYTHING BELOW THIS COMMENT
    
    # Checks the result of inc_arr, which should contain 2 3 4 5 6 after
    # one call.
    # You can safely ignore this function; it has no errors.
    check_arr:
        la t0, exp_inc_array_result
        la t1, array
        addi t2, t1, 20 # Last element is 5*4 bytes off
    check_arr_loop:
        beq t1, t2, check_arr_end
        lw t3, 0(t0)
        lw t4, 0(t1)
        bne t3, t4, failure
        addi t0, t0, 4
        addi t1, t1, 4
        j check_arr_loop
    check_arr_end:
        ret
        
    
    # This isn't really a function - it just prints a message, then
    # terminates the program on failure. Think of it like an exception.
    failure:
        li a0, 4 # String print ecall
        la a1, failure_message
        ecall
        li a0, 10 # Exit ecall
        ecall
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    • What caused the errors in simple_fn, naive_pow, and inc_arr that were reported by the Venus CC checker?

    在 simple_fn 中,t0t_0t0​ 是调用者保存的,然而在函数中调用需要赋一个初值

    在 naive_pow 中,使用了 s0(保存寄存器),但未在函数开头保存它

    在 inc_arr 中,使用了 s0 和 s1(保存寄存器),但未保存它们。调用 helper_fn 前未保存临时寄存器 t0(虽然 t0 是临时寄存器,但跨函数调用时需手动保存)。

    • In RISC-V, we call functions by jumping to them and storing the return address in the ra register. Does calling convention apply to the jumps to the naive_pow_loop or naive_pow_end labels? 在 RISC-V 中,我们通过跳转到函数并将返回地址存储在 ra 寄存器中来调用函数。调用约定是否适用于跳转到 naive_pow_loop 函数? 或 naive_pow_end 标签?

    不实用,ra 属于函数调用,而 naive_pow_loop 和 naive_pow_end 属于标签跳转

    • Why do we need to store ra in the prologue for inc_arr, but not in any other function? 为什么我们需要将 ra 存储在 inc_arr 的序言中,而不是存储在任何其他函数中?

    因为接下来需要调用 helper_fn 函数,必须在在开始的时候保存 ra

    • Why wasn’t the calling convention error in helper_fn reported by the CC checker? (Hint: it’s mentioned above in the exercise instructions.)

    不知道,不会,大佬教教

    # Exercise 5: RISC-V function calling with map

    .globl map
    
    .text
    main:
        jal ra, create_default_list
        add s0, a0, x0  # a0 = s0 is head of node list
    
        #print the list
        add a0, s0, x0
        jal ra, print_list
    
        # print a newline
        jal ra, print_newline
    
        # load your args
        add a0, s0, x0  # load the address of the first node into a0
    
        # load the address of the function in question into a1 (check out la on the green sheet)
        la a1, square    # 加载 square 函数的地址到 a1
    
        # issue the call to map
        jal ra, map
    
        # print the list
        add a0, s0, x0
        jal ra, print_list
    
        # print another newline
        jal ra, print_newline
    
        addi a0, x0, 10
        ecall #Terminate the program
    
    map:
        # Prologue: Make space on the stack and back-up registers
        addi sp, sp, -12    # 分配栈空间(保存 ra, s0, s1)
        sw ra, 8(sp)        # 保存返回地址
        sw s0, 4(sp)        # 保存 s0
        sw s1, 0(sp)        # 保存 s1
    
        beq a0, x0, done    # If we were given a null pointer (address 0), we're done.
    
        add s0, a0, x0  # Save address of this node in s0
        add s1, a1, x0  # Save address of function in s1
    
        # load the value of the current node into a0
        lw a0, 0(s0)    # 加载当前节点的值到 a0(作为函数参数)
    
        # Call the function in question on that value. DO NOT use a label (be prepared to answer why).
        jalr ra, s1, 0  # 调用函数(地址在 s1 中),结果存储在 a0
    
        # store the returned value back into the node
        sw a0, 0(s0)    # 将返回值存回当前节点的 value
    
        # Load the address of the next node into a0
        lw a0, 4(s0)    # 加载下一个节点的地址到 a0
    
        # Put the address of the function back into a1 to prepare for the recursion
        add a1, s1, x0  # 将函数地址从 s1 恢复到 a1
    
        # recurse
        jal ra, map     # 递归调用 map
    
    done:
        # Epilogue: Restore register values and free space from the stack
        lw s1, 0(sp)    # 恢复 s1
        lw s0, 4(sp)    # 恢复 s0
        lw ra, 8(sp)    # 恢复 ra
        addi sp, sp, 12 # 释放栈空间
        jr ra           # Return to caller
    
    square:
        mul a0, a0, a0
        jr ra
    
    # ... (其余代码保持不变)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    CS61C Lab2
    CSAPP 学习笔记

    ← CS61C Lab2 CSAPP 学习笔记→

    最近更新
    01
    在 ACM 集训队年会上的讲话
    07-01
    02
    计算机网络笔记
    06-13
    03
    LLM101 NLP学习笔记
    06-02
    更多文章>
    Theme by Vdoing | Copyright © 2024-2025 Martian148 | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式