如何在 x86-64 程序集中交换二维数组的 2 个值

how to swap 2 values of a two dimensional array in x86-64 assembly

我正在用汇编 x86-64 编写一个程序,该程序根据 c

中的以下函数转置矩阵
void transpose_matrix_by_naive(int **matrix, const int matrixSize){

    int i, j, auxValue;

    for (i = 0; i < matrixSize; i++){
        for (j = 0; j < matrixSize; j++){
            if (i < j){
                auxValue = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = auxValue;
            }
        }
    }
}

创建矩阵我使用以下函数

void create_matrix(int ***matrix, const int matrixSize){

    int i;

    *matrix = (int **)malloc(matrixSize * sizeof(int *));
    for (i = 0; i < matrixSize; i++){
        (*matrix)[i] = (int *)malloc(matrixSize * sizeof(int));
    }    
}

asm中的程序是

;extern void transpose_matrix_by_naive_in_asm(int **matrix, int matrixSize);
;                                                   RDI         RSI
section .text
    global transpose_matrix_by_naive_in_asm
transpose_matrix_by_naive_in_asm:
    mov RAX, 0
    mov RBX, 0
loop_for_i:
    loop_for_j:
        cmp RAX, RBX
        jl conditional_if
        return:
        inc RBX
        cmp RBX, RSI
        jne loop_for_j
    inc RAX
    cmp RAX, RSI
    jne loop_for_i
conditional_if:
    ;here I have to make a swap
    jmp return

我的问题是“如何交换矩阵 [i] [j] 和矩阵 [j] [i] 的值?”。值得一提的是,我只使用方矩阵。

在 x86 汇编程序中,内存中的两个值可以简单地交换

 mov eax, [rdi]
 mov ebx, [rsi]
 mov [rdi], ebx
 mov [rsi], eax
 

其中rdirsi是两个32位整数的内存地址。 地址计算通常应该通过计算 步幅 或偏移量来加快,为了使第 N 个元素正确或向下。

汇编程序中的整个矩阵转置可能最好由编译器生成。 OTOH 编译器在将操作分解为 4x4 子矩阵块时可能无法使用 SIMD 指令:

for (y = 0; y < size/4; y++) {
    for (x = 0; x < y; x++) {
        if (y == x) {
            transposeSubBlock(matrix, y);
        } else if (y < x) {
            transposeSubBlock(matrix, y, x);
        }
    }
}

现在,通过加载 4 个 SIMD 寄存器(从相邻的行和相邻的列)、转置寄存器并回写,可以在矩阵对角线上转置单个 4x4 块。

__m128i a[4];
a[0] = _mm_loadu_si128(row + 0 * stride);
a[1] = _mm_loadu_si128(row + 1 * stride);
a[2] = _mm_loadu_si128(row + 2 * stride);
a[3] = _mm_loadu_si128(row + 3 *stride);
transpose(a[0],a[1],a[2],a[3]);
_mm_storeu_si128(row + 0 * stride, a[0]);
_mm_storeu_si128(row + 1 * stride, a[1]);
_mm_storeu_si128(row + 2 * stride, a[2]);
_mm_storeu_si128(row + 3 * stride, a[3]);

其他块通过加载两个 4x4 块来处理,将两个块转置到寄存器中,然后写入相反的目标。

__m128i a[4], b[4];
load4vectors(a, src0, stride);
load4vectors(b, src1, stride);
transpose(a[0],a[1],a[2],a[3]);
transpose(b[0],b[1],b[2],b[3]);
store4vectors(src0, b[0],b[1],b[2],b[3]);
store4vectors(src1, a[0],a[1],a[2],a[3]);

转置 int[4][4] 矩阵的一种可能方法是将每个元素转换为 __m128 应用 MM_TRANSPOSE_PS(a,b,c,d); 宏并将每个元素转换回 __m128i

剩下的任务是处理多余的元素,它们不是 4 的倍数。