0

I'm working on a program in ARM64 gcc 11.2 Assembly and am experiencing a bug that I do not know how to fix. The goal of this program is to recursively call a function on a double array of numbers, then print out the highest double. In C, this function is:

#include <stdio.h>

#define SIZE 10 
         
double arr[SIZE] = {6.221, 1.0,  14.1, 46.5, 62.1, 7.7, 4.4, 1.5, 500.023, 2.1}; // get the array

double max_value(double array[], int size) {
  
    if (size == 1) {
        return array[size - 1];
    } else {
        double max = max_value(array, size - 1);
        if (array[size - 1] > max)
            max = array[size - 1]; // iterate to the next index of the array
        return max;
    }
}

int main() {
    printf("%g\n", max_value(arr, SIZE)); // print`
    return 0;
}


With that in mind, I've written the following in ARM64 gcc 11.2:




.global _start // https://stackoverflow.com/questions/17898989/what-is-global-start-in-assembly-language
       // global_start adds _start to the object code, global is NASM specific
       // and it will start the file

.text 

.extern printf // start the file 
maxvalue:
        stp     x29, x30, [sp, -48]!
        mov     x29, sp
        str     x0, [sp, 24]
        str     w1, [sp, 20]
        ldr     w0, [sp, 20]
        cmp     w0, 1
        bne     .L2
        ldr   x0, [sp, 20]
        lsl     x0, x0, 3
        sub     x0, x0, #8
        ldr     x1, [sp, 24]
        add     x0, x1, x0
        ldr     d0, [x0]
        b       .L3
.L2:
        ldr     w0, [sp, 20]
        sub     w0, w0, #1
        mov     w1, w0
        ldr     x0, [sp, 24]
        bl      maxvalue
        str     d0, [sp, 40]
        ldr   x0, [sp, 20]
        lsl     x0, x0, 3
        sub     x0, x0, #8
        ldr     x1, [sp, 24]
        add     x0, x1, x0
        ldr     d0, [x0]
        ldr     d1, [sp, 40]
        fcmpe   d1, d0
        bmi     .L6
        b       .L4
.L6:
        ldr   x0, [sp, 20]
        lsl     x0, x0, 3
        sub     x0, x0, #8
        ldr     x1, [sp, 24]
        add     x0, x1, x0
        ldr     d0, [x0]
        str     d0, [sp, 40]
.L4:
        ldr     d0, [sp, 40]
.L3:
        ldp     x29, x30, [sp], 48
        
.LC0:
        .string "%g\n"
_start:
        stp     x29, x30, [sp, -16]!
        mov     x29, sp
        mov     w1, 10
        adrp    x0, arr
        add     x0, x0, :lo12:arr
        bl      maxvalue
        adrp    x0, .LC0
        add     x0, x0, :lo12:.LC0
        bl      printf
        // print a new line
        mov     w0, 10
        bl      putchar


        mov     w0, 0
        ldp     x29, x30, [sp], 16
         

.data
// declare an array - the one I'm using is below
//double arr[SIZE] = {6.221, 1.0,  14.1, 46.5, 62.1, 7.7, 4.4, 1.5, 500.023, 2.1}; // get the array
 
arr:
        .word   -755914244
        .word   1075372621
        .word   0
        .word   1072693248
        .word   858993459
        .word   1076638515
        .word   0
        .word   1078411264
        .word   -858993459
        .word   1078922444
        .word   -858993459
        .word   1075760332
        .word   -1717986918
        .word   1074895257
        .word   0
        .word   1073217536
        .word   893353198
        .word   1082081374
        .word   -858993459
        .word   1073794252



I'm getting these errors (screenshots with the terminal commands that I'm using & errors generated).

enter image description here

enter image description here

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 1
    Copy/paste from the terminal into code blocks, like you did for the code, not images of text. And more importantly, use a debugger to see which instruction is faulting; that's a key part of a [mcve]. qemu-user can act as a GDB remote so you can even single-step instead of just producing a core dump. See [How to single step ARM assembly in GDB on QEMU?](https://stackoverflow.com/q/20590155) and [How to run a single line of assembly, then see \[R1\] and condition flags](https://stackoverflow.com/q/39503997) for examples (those are 32-bit ARM, but should be the same for ARM64). – Peter Cordes Apr 19 '22 at 01:55
  • 1
    First observation: you've omitted the `ret` at the end of `maxvalue`, so when it finishes it will fall through back into the beginning of `_start`. You also don't have a `ret` at the end of `_start`, but in that case it wouldn't help, because `_start` isn't called as a function and there is nowhere to return to. You have to invoke the `_exit` system call instead with the appropriate `svc` call for your host operating system. See https://stackoverflow.com/questions/49674026/what-happens-if-there-is-no-exit-system-call-in-an-assembly-program – Nate Eldredge Apr 19 '22 at 05:46
  • And also https://stackoverflow.com/questions/19760002/nasm-segmentation-fault-on-ret-in-start which is the same issue arising on x86. – Nate Eldredge Apr 19 '22 at 05:47
  • Also by the way, the `fmax` instruction would make your life much easier - but maybe that would take the fun out of it. – Nate Eldredge Apr 19 '22 at 06:28
  • On arm64 varargs are padded to multiple of 8, not 4 (other argument to 4 still) – Swift - Friday Pie Apr 19 '22 at 08:48
  • 1
    If you post assembly code, always make sure to comment it well so others can see what you intend to do and match that with what it actually does – fuz Apr 19 '22 at 10:06

0 Answers0