You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Y86架构冒泡排序程序排序异常问题排查与修正咨询

Y86冒泡排序代码错误分析与修正

问题背景

将C语言冒泡排序转换为Y86汇编时,处理10个元素的数组无法正确排序,但元素数量少于5个时排序正常。

原始C代码

void bubbleSort2(long arr[], long len) {
    long i;         // inner loop index
    long tmp;       // temp for swapping
    long *arr_curr; // pointer to element in arr
    long *arr_next; // pointer to next element in arr

    while (len > 1) {
        arr_curr = arr; // begin at the start of arr
        i = 0;
        while (i < len-1) {
            arr_next = arr_curr + 1;  // set next pointer
            if (*arr_curr > *arr_next) {
                // Swap the two elements
                // to bubble up the larger value
                tmp = *arr_curr;
                *arr_curr = *arr_next;
                *arr_next = tmp;
            }
            i++;
            arr_curr = arr_next;  // move to the next element 
        }
        len--;
    }
}

现有Y86汇编代码

# Execution begins at address 0 
    .pos 0 
    irmovq stack, %rsp   # Set up stack pointer  
    call main            # Execute main program
    halt                 # Terminate program 

# Array
    .align 8
arr:
    .quad 0x64
    .quad 0x34
    .quad 0x40
    .quad 0x25
    .quad 0x12
    .quad 0x22
    .quad 0x11
    .quad 0x90
    .quad 0x55
    .quad 0x42

main:
    irmovq arr, %rdi    
    irmovq $10, %rsi
    call bubble
    ret 

# start bubble
bubble:
   irmovq $1, %rax
outer_loop:
   rrmovq %rsi, %r8
   subq %rax, %r8
   irmovq $1, %r9
   irmovq $0, %rcx
inner_loop:
   rrmovq %rdi, %r10
   addq %rcx, %r10
   mrmovq (%r10), %r11
   mrmovq 8(%r10), %r12
   subq %r12, %r11
   jle no_swap
   subq %r11, %r12
   je no_swap
   jg swap
swap:
   mrmovq (%r10), %r11
   mrmovq 8(%r10), %r12
   rmmovq %r11, 8(%r10)
   rmmovq %r12, (%r10)
no_swap:
   irmovq $8, %r13
   addq %r13, %rcx
   subq %rax, %r8
   jg inner_loop
   rrmovq %rdi, %r10
   subq %rax, %r8
   jg outer_loop
   ret
# end bubble
# The stack starts here and grows to lower addresses
    .pos 0x200      
stack:

运行后内存变化结果

Changes to memory:
0x0018: 0x0000000000000064  0x0000000000000034
0x0020: 0x0000000000000034  0x0000000000000040
0x0028: 0x0000000000000040  0x0000000000000025
0x0030: 0x0000000000000025  0x0000000000000012
0x0038: 0x0000000000000012  0x0000000000000022
0x0040: 0x0000000000000022  0x0000000000000011
0x0048: 0x0000000000000011  0x0000000000000064
0x0050: 0x0000000000000090  0x0000000000000055
0x0058: 0x0000000000000055  0x0000000000000042
0x0060: 0x0000000000000042  0x0000000000000090
0x01f0: 0x0000000000000000  0x0000000000000085
0x01f8: 0x0000000000000000  0x0000000000000013

错误原因

  1. 内外层循环终止逻辑混乱:
    现有代码用r8同时控制内外层循环的终止,每次内层循环都会修改r8的值,导致循环次数计算完全偏离C代码逻辑。原C代码中内层循环是i < len-1,外层循环是len > 1,两者逻辑独立,不能共用同一个寄存器判断。
  2. 冗余比较操作:
    subq %r11, %r12和je no_swap属于重复判断,之前的subq %r12, %r11和jle no_swap已经覆盖了*arr_curr <= *arr_next的所有情况(包括相等)。
  3. Swap操作重复读取内存:
    进入swap块后再次执行mrmovq读取同一地址,完全可以复用之前已经读取到r11和r12的值,既浪费周期又容易出错。

修正后的Y86汇编代码

# Execution begins at address 0 
    .pos 0 
    irmovq stack, %rsp   # Set up stack pointer  
    call main            # Execute main program
    halt                 # Terminate program 

# Array
    .align 8
arr:
    .quad 0x64
    .quad 0x34
    .quad 0x40
    .quad 0x25
    .quad 0x12
    .quad 0x22
    .quad 0x11
    .quad 0x90
    .quad 0x55
    .quad 0x42

main:
    irmovq arr, %rdi    
    irmovq $10, %rsi
    call bubble
    ret 

# start bubble
bubble:
    # 保存调用者寄存器(Y86调用约定:rdi、rsi为调用者保存,需自行保存)
    pushq %rdi
    pushq %rsi

outer_loop:
    # 恢复len和数组基地址,判断外层循环条件:len > 1
    popq %rsi
    popq %rdi
    irmovq $1, %r8
    cmpq %r8, %rsi
    jle bubble_end       # len <=1,退出外层循环
    
    # 保存当前len和基地址,准备内层循环
    pushq %rdi
    pushq %rsi
    
    irmovq $0, %rcx      # i=0,对应字节偏移量初始为0
    subq %r8, %rsi       # 计算len-1,用于内层循环终止判断
    irmovq $8, %r9
    mulq %r9, %rsi       # 转换为字节偏移:(len-1)*8,即内层循环的最大偏移
    rrmovq %rdi, %r10    # 缓存数组基地址,避免重复加载

inner_loop:
    cmpq %rsi, %rcx      # 判断当前偏移是否小于最大偏移(i < len-1)
    jge inner_end        # 达到终止条件,退出内层循环
    
    # 读取当前元素和下一个元素
    mrmovq (%r10, %rcx), %r11
    mrmovq 8(%r10, %rcx), %r12
    
    # 判断是否需要交换
    subq %r12, %r11
    jle no_swap          # *arr_curr <= *arr_next,跳过交换
    
    # 执行交换,复用已读取的寄存器值
    rmmovq %r12, (%r10, %rcx)
    rmmovq %r11, 8(%r10, %rcx)

no_swap:
    irmovq $8, %r8
    addq %r8, %rcx       # i++,偏移量加8
    jmp inner_loop

inner_end:
    # 外层循环:len--,重新进入外层判断
    popq %rsi
    popq %rdi
    irmovq $1, %r8
    subq %r8, %rsi       # len = len -1
    pushq %rdi
    pushq %rsi
    jmp outer_loop

bubble_end:
    # 恢复调用者寄存器并返回
    pushq %rdi
    pushq %rsi
    popq %rsi
    popq %rdi
    ret
# end bubble
# The stack starts here and grows to lower addresses
    .pos 0x200      
stack:

关键修正说明

  1. 对齐C代码循环逻辑:
    用栈保存每次的len和数组基地址,确保外层循环严格按照len >1判断,内层循环按照i < len-1执行,逻辑和C代码完全一致。
  2. 移除冗余操作:
    删除重复的内存读取和比较判断,提升代码效率和可读性。
  3. 寄存器使用优化:
    缓存数组基地址到r10,避免每次内层循环都重新加载rdi,减少指令周期。

内容的提问来源于stack exchange,提问作者chronkilo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 22:27:00