gpt4 book ai didi

c++ - C++ 的哪些功能使用了 JR 指令?

转载 作者:行者123 更新时间:2023-11-28 04:07:23 24 4
gpt4 key购买 nike

MIPS 跳转寄存器 (JR) 指令经常出现在 C++ 代码的二进制文件中。那么,C++ 中的哪些功能使用了 JR 指令,为什么要使用这些指令?

最佳答案

分支指令只能用于目标地址在编译时已知并且在当前指令的小范围内的情况。您不能(轻松地)使用它来分支到一个静态未知的地址,并且必须在运行时计算/加载,或者跳转到太远的目标

下面是一些必须使用JRJALR 的例子(除了JALR 存储要返回的当前地址外,两者完全相同稍后):

  • 跳转到任意地址:静态分支指令不能用于跳转到 32 位或 64 位地址,因为立即数只有 16 或 26 位长。您需要将完整地址加载到寄存器中并使用 JR/JALR

    跳转
  • Function pointers : 调用函数只在运行时才知道,所以显然你需要一些方法来动态调用它

    int Add(int a, int b);
    int Sub(int a, int b);
    int Mul(int a, int b);
    int Div(int a, int b);
    int (*p[4]) (int x, int y) = { Add, Sub, Mul, Div };

    int test_function_pointer(int i, int x, int y) {
    return p[i](x, y);
    }

    shared libraries 中的函数(*.dll, *.so...) 在加载之前对进程也是未知的,因此如果您手动加载这些库(使用 LoadLibrary()dlopen() ...),您还将获得地址到一个函数指针,并用 JR/JALR 调用它们。通常情况下,函数将使用 JALR 调用,但如果它位于函数的末尾并且 tail-call optimization启用后将使用 JR

    Vtable在C++等很多OOP语言中也是函数指针的一个例子

    struct A {
    virtual int getValue() = 0;
    };

    int test_vtable(A *a) {
    return a->getValue() + 1;
    }

    Demo on Godbolt's Compiler Explorer

  • Jump table (就像在一个大开关 block 中)

    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 block 。它的地址存储在$2中,需要使用jr将指令指针移动到该地址。 j $2 如上所示,但我认为这是一个反汇编程序错误,因为 j 无法接收寄存器操作数。一旦你处于正确的情况下,jr 将再次用于调用 f 函数指针

另见 Necessity of J vs. JAL (and JR vs. JALR) in MIPS assembly

关于c++ - C++ 的哪些功能使用了 JR 指令?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58477929/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com