Question

Why are multiple B.Cond statements always resolving only one way for variable input?

Utilizing arm64, on qemu.

I want to check whether a user inputted value is negative, positive, or zero. I have a SUBS instruction, followed by three B.Cond instructions. I'm using scanf with a %d format to intake the variable, and printf to output the variable. This is happening in a toy program, with no other functions.

If I change the order of the B.Cond instructions, in case it's some default behavior due to my own ignorance or lack of attention to detail, the B.GT instruction is run.

If I change the value I use to compare the user value against, in case I'm missing some finer point of the conditional branch instruction, the B.GT instruction is run.

If I change the zero check instruction to CBZ, in case, again that it is some aspect of the B.Cond instruction, the B.GT instruction is run.

The following is the relevant code. I've omitted the portions related to printing which branch was taken.


.section .data

input_number:       .asciz  "Please enter a number: "
input_spec:         .asciz  "%d"
error:          .asciz "Please input a positive number \n"
negative:   .asciz "Input is NEGATIVE.\n"
zero:   .asciz "Input is ZERO.\n"
positive:   .asciz "Input is POSITIVE.\n"

.section .text


.global main



main:

LDR x0, =input_number
BL printf

SUB SP, SP, #16
LDR x0, =input_spec
MOV x1, SP
BL scanf
LDUR x9, [SP, #0]
ADD SP, SP, #16

CMP x9, xzr
B.GT POSITIVE
B.LT NEGATIVE
CBZ x10, ZERO

B exit

NEGATIVE:

LDR x0, =negative
BL printf
B exit


ZERO:

LDR x0, =zero
BL printf
B exit


POSITIVE:

LDR x0, =positive
BL printf
B exit


exit:

    mov x0, 0
    mov x8, 93
    svc 0
    ret

Editing in accordance with suggestions, and answering them. @Nate Eldridge: I've added the executing code at the end of the branches, and associated global variables.
I've edited the stack increment and decrement to be a multiple of 16. I had read that elsewhere, but doing so caused infinite subtraction in my original, recursive, program, as if it never recognized when the value that was being kept track of became zero, and so recursed indefinitely.
I'm aware of the MOV and CMP macros(instructions?). Since I'm still learning the instruction set, I want to learn the standard instructions before utilizing easier methods. I've changed the code per your suggestion for readability.
I originally did have CBZ as B.EQ, but changed it for testing purposes. I feel it's potentially useful to show that multiple types of conditional branch instructions are affected, not just B.Cond.
I feel I know which instructions are executing due to the originally left-out printf statements, yes.
I am learning from a textbook utilizing legv8, yes.

 3  44  3
1 Jan 1970

Solution

 3

You use the %d format specifier to scanf, which expects a pointer to a 32-bit word. However you then load it off the stack using LDUR x9, [SP, #0], which is a 64-bit load. So the low 32 bits of x9 (also known as w9) will contain the number, but the high 32 bits will contain garbage. If that garbage happens to have zero as its highest bit (which is likely given that all stack memory is zeroed at the start of the program), then the resulting signed 64-bit number will be positive.

Some ways to fix it:

  • Do a 32-bit load into a 64-bit register with sign extension: LDRSW x9, [SP]. (Or LDURSW if you prefer. You don't need to write the immediate offset #0 when it's zero.) Then x9 contains the 64-bit value that's numerically equal to the 32-bit value from the stack, and your compare will behave as expected.

  • Do a 32-bit load into a 32-bit register, and simply use 32-bit instructions after that: LDR w9, [SP] followed by CMP w9, wzr (or SUBS w10, w9, wzr as in your original version).

  • Have scanf parse a 64-bit long int value: use %ld instead of %d as your format string. Then the rest of the code can stay the same.

2024-07-02
Nate Eldredge