Skip to content

riscv-interrupt-m saves unused caller-saved registers (t0t2, …) in a bare-metal ISR #163231

Description

@XuanTongYao

Related: #111889

This is a bare-metal program. A minimal reproduction is attached.

The following Rust code generates incorrect assembly: the mtimer_IRQ_Handler interrupt handler saves caller-saved registers that it never uses (t0, t1, t2, and so on).

#![no_std]
#![no_main]
#![feature(abi_riscv_interrupt)]

core::arch::global_asm!(include_str!("../start.riscv"));
core::arch::global_asm!(include_str!("../init_bss.riscv"));
core::arch::global_asm!(include_str!("../trap.riscv"));

#[inline(never)]
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
    loop {}
}

use riscv::interrupt::Interrupt::*;
use riscv_macros::entry;
use volatile_register::RW;

#[repr(C)]
pub struct MtimeRegisterBlock {
    pub mtimel: RW<u32>,
    pub mtimeh: RW<u32>,
    pub mtimecmpl: RW<u32>,
    pub mtimecmph: RW<u32>,
}

pub struct Mtime(*mut MtimeRegisterBlock);

impl Mtime {
    pub unsafe fn singleton() -> Self {
        Self((0 as *mut u8).wrapping_byte_add(0x2040) as _)
    }

    pub const FREQ_HZ: u32 = 1_000_000;
    pub const fn sec_ticks(second: u64) -> u64 {
        second * Self::FREQ_HZ as u64
    }

    pub fn update_mtimecmp_forward(&mut self, ticks: u64) {
        let mut time = self.mtime();
        time += ticks;
        unsafe { self.set_mtimecmp(time) }
    }
}

#[cfg(target_arch = "riscv32")]
impl Mtime {
    pub fn mtime(&self) -> u64 {
        loop {
            let regs = unsafe { &*(self.0) };
            let high = regs.mtimeh.read();
            let low = regs.mtimel.read();
            if high == regs.mtimeh.read() {
                return ((high as u64) << 32) | (low as u64);
            }
        }
    }

    pub unsafe fn set_mtimecmp(&mut self, value: u64) {
        let high = (value >> 32) as u32;
        let low = value as u32;
        let regs = unsafe { &*(self.0) };
        unsafe {
            regs.mtimecmpl.write(u32::MAX);
            regs.mtimecmph.write(high);
            regs.mtimecmpl.write(low);
        }
    }
}

#[entry]
fn main() -> ! {
    unsafe {
        riscv::interrupt::enable_interrupt(MachineTimer);
        let mut mtime = Mtime::singleton();
        mtime.update_mtimecmp_forward(Mtime::sec_ticks(1));
        riscv::interrupt::enable();
    }
    loop {}
}

#[unsafe(no_mangle)]
unsafe extern "riscv-interrupt-m" fn mtimer_IRQ_Handler() {
    let mut mtime = unsafe { Mtime::singleton() };
    mtime.update_mtimecmp_forward(Mtime::sec_ticks(1));
}

The objdump disassembly is as follows:

riscv-int-abi-bug:      file format elf32-littleriscv

Disassembly of section .text:

00000000 <_start>:
       0: 00001197      auipc   gp, 0x1
       4: 97c18193      addi    gp, gp, -0x684

00000008 <.Lpcrel_hi0>:
       8: 00002117      auipc   sp, 0x2
       c: ff810113      addi    sp, sp, -0x8

00000010 <.Lpcrel_hi2>:
      10: 00000517      auipc   a0, 0x0
      14: 16c50513      addi    a0, a0, 0x16c

00000018 <.Lpcrel_hi3>:
      18: 00000597      auipc   a1, 0x0
      1c: 16458593      addi    a1, a1, 0x164
      20: 00b57863      bgeu    a0, a1, 0x30 <.Ltmp1>
      24: 00052023      sw      zero, 0x0(a0)
      28: 00450513      addi    a0, a0, 0x4
      2c: feb56ce3      bltu    a0, a1, 0x24 <.Lpcrel_hi3+0xc>

00000030 <.Ltmp1>:
      30: 00000517      auipc   a0, 0x0
      34: 01850513      addi    a0, a0, 0x18
      38: 00156513      ori     a0, a0, 0x1
      3c: 30551073      csrw    mtvec, a0
      40: 30401073      csrw    mie, zero
      44: 088000ef      jal     0xcc <main>

00000048 <_exception>:
      48: 0440006f      j       0x8c <UnhandledFault>

0000004c <_software_int>:
      4c: 03c0006f      j       0x88 <stimer_IRQ_Handler>
      50: 00 00 00 00   .word   0x00000000
      54: 0340006f      j       0x88 <stimer_IRQ_Handler>
      58: 00 00 00 00   .word   0x00000000

0000005c <_timer_int>:
      5c: 02c0006f      j       0x88 <stimer_IRQ_Handler>
      60: 00 00 00 00   .word   0x00000000
      64: 0880006f      j       0xec <mtimer_IRQ_Handler>
      68: 00 00 00 00   .word   0x00000000

0000006c <_extern_int>:
      6c: 01c0006f      j       0x88 <stimer_IRQ_Handler>
      70: 00 00 00 00   .word   0x00000000
      74: 0140006f      j       0x88 <stimer_IRQ_Handler>
      78: 00 00 00 00   .word   0x00000000
      7c: 00000013      nop
      80: 00000013      nop
      84: 00000013      nop

00000088 <stimer_IRQ_Handler>:
      88: 0000006f      j       0x88 <stimer_IRQ_Handler>

0000008c <UnhandledFault>:
      8c: 0000006f      j       0x8c <UnhandledFault>

00000090 <_RNvMCsabKyeRqXjkn_17riscv_int_abi_bugNtB2_5Mtime23update_mtimecmp_forward>:
      90: 00002537      lui     a0, 0x2
      94: 04452583      lw      a1, 0x44(a0)
      98: 04052603      lw      a2, 0x40(a0)
      9c: 04452683      lw      a3, 0x44(a0)
      a0: fed59ae3      bne     a1, a3, 0x94 <_RNvMCsabKyeRqXjkn_17riscv_int_abi_bugNtB2_5Mtime23update_mtimecmp_forward+0x4>
      a4: 000f46b7      lui     a3, 0xf4
      a8: 24068693      addi    a3, a3, 0x240
      ac: 00d606b3      add     a3, a2, a3
      b0: 00c6b633      sltu    a2, a3, a2
      b4: fff00713      li      a4, -0x1
      b8: 00c585b3      add     a1, a1, a2
      bc: 04e52423      sw      a4, 0x48(a0)
      c0: 04b52623      sw      a1, 0x4c(a0)
      c4: 04d52423      sw      a3, 0x48(a0)
      c8: 00008067      ret

000000cc <main>:
      cc: ff010113      addi    sp, sp, -0x10
      d0: 00112623      sw      ra, 0xc(sp)
      d4: 08000513      li      a0, 0x80
      d8: 30452073      csrs    mie, a0
      dc: fb5ff0ef      jal     0x90 <_RNvMCsabKyeRqXjkn_17riscv_int_abi_bugNtB2_5Mtime23update_mtimecmp_forward>
      e0: 00800513      li      a0, 0x8
      e4: 30052073      csrs    mstatus, a0
      e8: 0000006f      j       0xe8 <main+0x1c>

000000ec <mtimer_IRQ_Handler>:
      ec: fc010113      addi    sp, sp, -0x40
      f0: 02112e23      sw      ra, 0x3c(sp)
      f4: 02512c23      sw      t0, 0x38(sp)
      f8: 02612a23      sw      t1, 0x34(sp)
      fc: 02712823      sw      t2, 0x30(sp)
     100: 02a12623      sw      a0, 0x2c(sp)
     104: 02b12423      sw      a1, 0x28(sp)
     108: 02c12223      sw      a2, 0x24(sp)
     10c: 02d12023      sw      a3, 0x20(sp)
     110: 00e12e23      sw      a4, 0x1c(sp)
     114: 00f12c23      sw      a5, 0x18(sp)
     118: 01012a23      sw      a6, 0x14(sp)
     11c: 01112823      sw      a7, 0x10(sp)
     120: 01c12623      sw      t3, 0xc(sp)
     124: 01d12423      sw      t4, 0x8(sp)
     128: 01e12223      sw      t5, 0x4(sp)
     12c: 01f12023      sw      t6, 0x0(sp)
     130: f61ff0ef      jal     0x90 <_RNvMCsabKyeRqXjkn_17riscv_int_abi_bugNtB2_5Mtime23update_mtimecmp_forward>
     134: 03c12083      lw      ra, 0x3c(sp)
     138: 03812283      lw      t0, 0x38(sp)
     13c: 03412303      lw      t1, 0x34(sp)
     140: 03012383      lw      t2, 0x30(sp)
     144: 02c12503      lw      a0, 0x2c(sp)
     148: 02812583      lw      a1, 0x28(sp)
     14c: 02412603      lw      a2, 0x24(sp)
     150: 02012683      lw      a3, 0x20(sp)
     154: 01c12703      lw      a4, 0x1c(sp)
     158: 01812783      lw      a5, 0x18(sp)
     15c: 01412803      lw      a6, 0x14(sp)
     160: 01012883      lw      a7, 0x10(sp)
     164: 00c12e03      lw      t3, 0xc(sp)
     168: 00812e83      lw      t4, 0x8(sp)
     16c: 00412f03      lw      t5, 0x4(sp)
     170: 00012f83      lw      t6, 0x0(sp)
     174: 04010113      addi    sp, sp, 0x40
     178: 30200073      mret

Meta

rustc --version --verbose:

rustc 1.100.0-nightly (6bb1652a0 2026-09-22)
binary: rustc
commit-hash: 6bb1652a020e80cef79332741d89e996d71933c9
commit-date: 2026-09-22
host: x86_64-pc-windows-msvc
release: 1.100.0-nightly
LLVM version: 23.1.1
Backtrace

warning: unstable feature specified for `-Ctarget-feature`: `relax`
  |
  = note: this feature is not stably supported; its behavior can change in the future
  = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
  = note: for more information, see issue #162235 <https://github.com/rust-lang/rust/issues/162235>

warning: `riscv-int-abi-bug` (bin "riscv-int-abi-bug") generated 1 warning
    Finished `release` profile [optimized] target(s) in 0.02s

riscv-int-abi-bug.zip

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.needs-triageThis issue may need triage. Remove it if it has been sufficiently triaged.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions