Как прокомментировал Николь, многие вещи могут его использовать, но для одного простого примера это указатель функции или таблица переходов (как в большом блоке переключателей). Vtable в C ++ также является примером указателя функции
typedef int (*func)(int);
int doSomething(func f, int x, int y)
{
switch(x)
{
case 0:
return f(x + y);
case 1:
return f(x + 2*y);
case 2:
return f(2*x + y);
case 3:
return f(x - y);
case 4:
return f(3*x + y);
case 5:
return f(x * y);
case 6:
return f(x);
case 7:
return f(y);
default:
return 3;
}
}
GCC компилирует приведенный выше код в
doSomething(int (*)(int), int, int):
sltu $2,$5,8
beq $2,$0,$L2 # x >= 8: default case
move $25,$4
lui $2,%hi($L4)
addiu $2,$2,%lo($L4) # load address of $L4 to $2
sll $5,$5,2 # effective address = $L4 + x*4
addu $5,$2,$5
lw $2,0($5)
nop
j $2
nop
$L4:
.word $L11
.word $L5
.word $L6
.word $L7
.word $L8
.word $L9
.word $L10
.word $L11
$L11:
jr $25
move $4,$6
$L9:
sll $4,$6,2
jr $25
addu $4,$4,$6
# ... many more cases below
Полный вывод можно увидеть в Compiler Explorer
$L4
- это таблица переходов, содержащая адрес места, к которому вы переходите, то есть блоков case
в этом фрагменте. Его адрес хранится в $2
, и jr
необходимо использовать для перемещения указателя инструкции на этот адрес. j $2
показано выше, но я думаю, что это ошибка дизассемблера, так как j
не может получить операнд регистра. Как только вы попали в правильный регистр, тогда jr
снова используется для вызова указателя функции f
В общем, в любом случае, целевой адрес не известен статически и должен быть рассчитан / загружен ввремя выполнения тогда jr
должно быть использовано