RISC-V汇编运行时地址越界错误排查与修复求助
RISC-V汇编快速排序地址越界问题修复
错误核心原因
报错显示访问地址0x00000000,说明partition函数里的数组基地址寄存器s0的值为0,根源是参数传递完全错误:从quickSort到qSort再到partition的参数都未正确传递,同时存在逻辑分支写反、partition逻辑完全偏离Java实现的问题。
原始代码
Java快速排序实现
//declare an array Y1 and its length lenY1 static int Y1[] = {13, 101, 79, 23, 154, 4, 11, 38, 88, 45, 17, 94, 62, 1}; static int lenY1 = 14; public static void main(String[] args) { //Define main method quickSort(Y1, lenY1); //Call quickSort method for (int i = 0; i<lenY1; i++) //Loop for printing System.out.print(Y1[i] + " "); System.out.println(""); //Print the sorted array } public static void quickSort(int[] x, int n) { //Define quickSort method qSort(x,0,n-1); //Call qSort method with appropriate parameters } public static void qSort(int x[], int left, int right) { //Define qSort method int middle; if (left < right) { //Loop for sorting If left is less than right middle = partition(x, left, right); //middle = partition(x, left, right) qSort(x, left, middle); //qSort(x, left, middle) qSort(x, middle+1, right); //qSort(x, middle+1, right) } } public static int partition(int x[], int left, int right) { //Define partition method int pivot, l, r, temp; //Load arguments pivot = x[left]; //Choose pivot l = left-1; //Initialize variables r = right+1; while (true) { //Loop partition do {r--;} while (x[r] > pivot); //Loop right do {l++;} while (x[l] < pivot); //Loop left if (l < r) { //Swap temp = x[l]; x[l] = x[r]; x[r]= temp; } else return r; } }
报错的RISC-V汇编代码
# Declare Y1 array .data Y1: .word 13, 101, 79, 23, 154, 4, 11, 38, 88, 45, 17, 94, 62, 1 lenY1: .word 14 SortedMsg: .asciz "\nSorted Array: \n" .text main: # Call quickSort on Y1 la a0, Y1 # Load address of Y1 into a0 lw a1, lenY1 # Load length of Y1 into a1 jal ra, quickSort # Call quickSort # Print Y1 li a2, 0 # Initialize index to 0 print_loop: slli t0, a2, 2 # Multiply index by 4 add t0, t0, a0 # Add to base address of Y1 lw a0, 0(t0) # Load element into a0 li a7, 1 # Print integer ecall ecall addi a2, a2, 1 # Increment index blt a2, a1, print_loop # Loop if index less than length # Exit li a7, 10 # Exit ecall ecall quickSort: addi sp, sp, -16 # Allocate stack space sw ra, 0(sp) # Save return address sw s0, 4(sp) # Save s0 sw s1, 8(sp) # Save s1 sw s2, 12(sp) # Save s2 mv s0, a0 # Save array address mv s1, a1 # Save length jal ra, qSort # Call qSort lw ra, 0(sp) # Restore return address lw s0, 4(sp) # Restore s0 lw s1, 8(sp) # Restore s1 lw s2, 12(sp) # Restore s2 addi sp, sp, 16 # Deallocate stack space ret # Return qSort: mv s2, a0 # Save array address mv s0, a1 # Save left index mv s1, a2 # Save right index blt s0, s1, qsort_done # If left greater than or equal to right, done jal ra, partition # Call partition mv a0, s2 # Pass array address mv a1, s0 # Pass left index mv a2, a0 # Pass partition index jal ra, qSort # Recursive call on left mv a0, s2 # Pass array address mv a1, a0 # Pass partition index addi a2, a0, 1 # Pass right index mv s1, a2 jal ra, qSort # Recursive call on right qsort_done: ret # Return partition: mv s0, a0 # Save array address mv s1, a1 # Save left index mv s2, a2 # Save right index lw t0, 0(s0) # Load pivot value **This is the error line** mv a0, t0 # Save pivot in a0 mv t1, s1 # Initialize i to left index partition_loop: blt t1, s2, partition_done # If i greater than or equal to right, done lw t2, 0(s0) # Load arr[i] bgt t2, a0, if_greater # If arr[i] greater than pivot, branch addi t1, t1, -1 # Increment i j partition_loop # Loop if_greater: lw t3, 0(s1) # Load arr[j] blt t3, a0, if_less # If arr[j] less than pivot, branch mv t4, t1 # t4 = i addi t4, t4, -1 # t4 = i - 1 slli t5, t4, 2 # t5 = 4 * (i -1) add t5, t5, s0 # t5 = &arr[i-1] lw t5, 0(t5) # Load arr[i-1] sw t3, 0(s1) # arr[j] = arr[i-1] sw t5, 0(t4) # arr[i-1] = arr[j] addi t1, t1, -1 # Decrement i j partition_loop # Loop if_less: addi t1, t1, 1 # Decrement i j partition_loop # Loop partition_done: mv a0, t1 ret # Return
具体修复点
quickSort参数传递错误:Java中quickSort需调用qSort(x, 0, n-1),汇编调用qSort前要设置三个参数:a0为数组地址,a1为0,a2为数组长度减1。qSort分支条件写反:原代码blt s0, s1, qsort_done逻辑与Java相反,改为bge s0, s1, qsort_done(当left >= right时直接返回)。qSort调用partition前未传参:调用partition前必须将数组地址、left、right存入a0/a1/a2,否则partition拿到垃圾值。qSort递归调用参数错误:需先保存partition的返回值,再作为递归调用的参数传递。partition逻辑完全重写:严格按照Java的双指针循环、交换逻辑实现,修正原逻辑与Java不符的问题。
修复后的完整RISC-V汇编代码
# Declare Y1 array .data Y1: .word 13, 101, 79, 23, 154, 4, 11, 38, 88, 45, 17, 94, 62, 1 lenY1: .word 14 Space: .asciz " " # 用于打印分隔符 .text main: # Call quickSort on Y1 la a0, Y1 # Load address of Y1 into a0 lw a1, lenY1 # Load length of Y1 into a1 jal ra, quickSort # Print Y1 li a2, 0 # Initialize index to 0 la a0, Y1 # 重新加载数组地址到a0 lw a1, lenY1 # 重新加载长度到a1 print_loop: slli t0, a2, 2 # Multiply index by 4 (int占4字节) add t0, t0, a0 # 计算当前元素地址 lw a0, 0(t0) # Load element into a0 li a7, 1 # 打印整数的系统调用号 ecall # 打印空格分隔 la a0, Space li a7, 4 ecall addi a2, a2, 1 # Increment index blt a2, a1, print_loop # Loop if index less than length # Exit li a7, 10 # Exit ecall ecall quickSort: addi sp, sp, -16 # Allocate stack space (保存ra, s0-s2) sw ra, 0(sp) sw s0, 4(sp) sw s1, 8(sp) sw s2, 12(sp) mv s0, a0 # 保存数组地址 mv s1, a1 # 保存数组长度 # 设置qSort的参数: a0=数组地址, a1=0, a2=len-1 mv a0, s0 li a1, 0 addi a2, s1, -1 jal ra, qSort # 恢复寄存器 lw ra, 0(sp) lw s0, 4(sp) lw s1, 8(sp) lw s2, 12(sp) addi sp, sp, 16 # 释放栈空间 ret qSort: addi sp, sp, -20 # 保存ra, s0-s3 (需要保存partition的返回值) sw ra, 0(sp) sw s0, 4(sp) # s0 = 数组地址 sw s1, 8(sp) # s1 = left sw s2, 12(sp) # s2 = right sw s3, 16(sp) # s3 = partition返回的middle mv s0, a0 mv s1, a1 mv s2, a2 # 如果left >= right, 直接返回 bge s1, s2, qsort_done # 调用partition: 参数a0=数组, a1=left, a2=right mv a0, s0 mv a1, s1 mv a2, s2 jal ra, partition mv s3, a0 # 保存partition的返回值r到s3 # 递归调用左半部分: qSort(x, left, middle) mv a0, s0 mv a1, s1 mv a2, s3 jal ra, qSort # 递归调用右半部分: qSort(x, middle+1, right) mv a0, s0 mv a1, s3 addi a1, a1, 1 # middle+1 mv a2, s2 jal ra, qSort qsort_done: # 恢复寄存器 lw ra, 0(sp) lw s0, 4(sp) lw s1, 8(sp) lw s2, 12(sp) lw s3, 16(sp) addi sp, sp, 20 ret partition: addi sp, sp, -20 # 保存ra, s0-s3 (数组地址, left, right, pivot) sw ra, 0(sp) sw s0, 4(sp) # s0 = 数组地址 sw s1, 8(sp) # s1 = left sw s2, 12(sp) # s2 = right sw s3, 16(sp) # s3 = pivot mv s0, a0 mv s1, a1 mv s2, a2 # 取pivot = x[left] slli t0, s1, 2 # left *4 add t0, t0, s0 # &x[left] lw s3, 0(t0) # s3 = pivot # 初始化l = left-1, r = right+1 addi t1, s1, -1 # t1 = l addi t2, s2, 1 # t2 = r partition_loop: # do {r--;} while(x[r] > pivot) r_decrement: addi t2, t2, -1 slli t0, t2, 2 add t0, t0, s0 lw t3, 0(t0) bgt t3, s3, r_decrement # do {l++;} while(x[l] < pivot) l_increment: addi t1, t1, 1 slli t0, t1, 2 add t0, t0, s0 lw t3, 0(t0) blt t3, s3, l_increment # 如果l >= r, 返回r bge t1, t2, partition_return_r # 否则交换x[l]和x[r] # 取x[l] slli t0, t1, 2 add t0, t0, s0 lw t4, 0(t0) # 取x[r] slli t5, t2, 2 add t5, t5, s0 lw t6, 0(t5) # 交换 sw t6, 0(t0) sw t4, 0(t5) # 继续循环 j partition_loop partition_return_r: mv a0, t2 # 返回r的值到a0 # 恢复寄存器 lw ra, 0(sp) lw s0, 4(sp) lw s1, 8(sp) lw s2, 12(sp) lw s3, 16(sp) addi sp, sp, 20 ret
修复说明
- 严格按照Java逻辑修正了所有函数的参数传递流程,确保数组地址、左右边界正确传递。
- 完全重写
partition函数,实现了Java中双指针循环、交换的逻辑,包括do-while循环的边界处理。 - 增加打印空格的逻辑,让排序后的输出更易读。
- 所有递归调用前都正确保存了返回地址和寄存器状态,避免栈溢出或寄存器值被覆盖。
内容的提问来源于stack exchange,提问作者MacKenna Bochnak
相关产品推荐
相关产品推荐

