diff --git a/include/vm_asm_opt_riscv64.h b/include/vm_asm_opt_riscv64.h index 4190298..2ab14be 100644 --- a/include/vm_asm_opt_riscv64.h +++ b/include/vm_asm_opt_riscv64.h @@ -122,10 +122,23 @@ static inline cell_t vm_pop_asm(VM *vm) { "lw t0, %[dsp]\n\t" "bltz t0, 1f\n\t" /* branch if dsp < 0 (underflow) */ "slli t1, t0, 3\n\t" - "add t1, %[stack], t1\n\t" - "ld %[val], 0(t1)\n\t" + "add t1, %[stack], t1\n\t" /* t1 = &data_stack[dsp] (old dsp) */ "addi t0, t0, -1\n\t" - "sw t0, %[dsp]\n\t" + "sw t0, %[dsp]\n\t" /* write dsp back BEFORE loading the + value: %[dsp] is a memory operand + referenced twice in this template + (read above, write here); loading + %[val] (a register output) before + this write-back let clang's + allocator reuse %[dsp]'s address + register for %[val], corrupting + the write-back — GCC happened to + pick different registers and + never hit it. Loading %[val] last + means %[dsp]'s final use has + already happened by the time the + output register is live. */ + "ld %[val], 0(t1)\n\t" "sw zero, %[err]\n\t" "j 2f\n\t" "1:\n\t" @@ -185,10 +198,11 @@ static inline cell_t vm_rpop_asm(VM *vm) { "lw t0, %[rsp]\n\t" "bltz t0, 1f\n\t" "slli t1, t0, 3\n\t" - "add t1, %[stack], t1\n\t" - "ld %[val], 0(t1)\n\t" + "add t1, %[stack], t1\n\t" /* t1 = &return_stack[rsp] (old rsp) */ "addi t0, t0, -1\n\t" - "sw t0, %[rsp]\n\t" + "sw t0, %[rsp]\n\t" /* write rsp back before loading the + value — see vm_pop_asm for why */ + "ld %[val], 0(t1)\n\t" "sw zero, %[err]\n\t" "j 2f\n\t" "1:\n\t"