DEV Community

ddupard
ddupard

Posted on

RV32I vs x86_32: How Branching Works Across Architectural Lines

Introduction

In control flow operations—such as jumps (jmp), branch instructions, and function calls (call)—the distinction between CISC and RISC architectures becomes immediately visible.

In a variable-length CISC architecture like x86, the processor can encode absolute 32-bit addresses directly into jump instructions. Conversely, a fixed-length 32-bit RISC architecture like RISC-V (RV32I) must fit every instruction, including its opcode, target registers, and offset payload, into exactly 4 bytes (32 bits).

This constraint introduces a fundamental challenge: How do you execute a long jump or function call to an arbitrary address in a 32-bit memory space when instructions are limited to 32 bits?


How x86_32 Handles Jumps and Calls

On x86_32, jumps and calls can encode either relative or absolute 32-bit offsets directly inside the instruction stream.

.intel_syntax noprefix
.section .text
.global _start

_start:
    call 0x12345678    # Call a function located at address 0x12345678
    jmp  0x87654321    # Absolute / Far Jump to 0x87654321

Enter fullscreen mode Exit fullscreen mode

When assembled and disassembled, x86 produces single multi-byte instructions:

08049000 <_start>:
8049000:  e8 73 66 2f 0a        call   12345678 <_start+0x0a2f6678>
8049005:  ea 21 43 65 87 1a 00  jmp    001a:87654321

Enter fullscreen mode Exit fullscreen mode

The x86 architecture seamlessly embeds the full target address or relative offset into 5-byte to 7-byte instruction encodings.


How RISC-V (RV32I) Handles Long Jumps and Calls

Because an RV32I instruction cannot exceed 32 bits, a single instruction cannot encode a full 32-bit target address.

To solve this, RISC-V provides two primary hardware instructions for jumps:

  • jal (Jump and Link, J-Type format): Uses a 20-bit immediate field (sign-extended and shifted left by 1 bit), allowing relative jumps within a ±1 MiB range around the current program counter (pc).
  • jalr (Jump and Link Register, I-Type format): Adds a 12-bit signed immediate offset to a base register.

When a jump target exceeds the ±1 MiB reach of a single jal, the RISC-V assembler converts pseudo-instructions like call or tail into a two-instruction pair.

.section .text
.global _start

_start:
    call target_func    # Pseudo-instruction for function call

Enter fullscreen mode Exit fullscreen mode

If target_func is located beyond the 1 MiB limit (e.g., at address 0x12345678), the assembler expands call into the following sequence:

00010074 <_start>:
   10074: 123450e7            auipc  ra, 0x12345
   10078: 6780e0e7            jalr   ra, 1656(ra)   # 1656 = 0x678

Enter fullscreen mode Exit fullscreen mode

Understanding auipc + `jalr`

The combined sequence works through two distinct steps:

  1. auipc ra, 0x12345 (Add Upper Immediate to PC): Adds the upper 20 bits shifted left by 12 bits to the current value of the program counter (pc) and stores the result in register ra (Return Address).
  2. jalr ra, 1656(ra) (Jump and Link Register): Adds the remaining sign-extended 12-bit offset (0x678 = 1656) to ra, saves pc + 4 into ra, and jumps to the calculated target.

This combination allows RISC-V to execute PC-relative calls anywhere within a ±2 GiB address space using two 32-bit instructions (8 bytes in total).


The Sign-Extension Trap on Relocations

Just like loading constants with lui + addi, auipc + jalr is vulnerable to sign-extension issues.

If bit 11 of the 12-bit offset in jalr is 1 (i.e., the offset is negative), jalr will subtract from the address pre-calculated by auipc. To prevent incorrect branching, the linker or assembler must automatically compensate by adding 1 to the 20-bit upper immediate passed to auipc.


Architectural Comparison

Feature CISC (x86_32) RISC (RISC-V RV32I)
Max Reach of Single Jump Full 32-bit address space (4 GiB) ±1 MiB (jal relative)
Long Call Sequence Single call instruction (5 bytes) auipc + jalr pair (8 bytes total)
Relocation Style Absolute or PC-relative Fully PC-relative (Position Independent Code by design)
Hardware Impact Variable-length instruction decoding Uniform 32-bit pipeline fetching and simplified branching logic

Epilogue

While x86 relies on complex decoding hardware to handle multi-byte branch targets inline, RISC-V enforces strict structural simplicity. By coupling auipc and jalr, RISC-V achieves fully position-independent control flow across the entire memory space without requiring complex multi-length instruction decoders.

Top comments (0)