为什么调用站点上的注册与实现站点上的注册不同?

问题描述 投票:0回答:1

让我们考虑一个简单的函数:

fun main() {
    greet("world")    
}

fun greet(name: String?) {
    if (name != null) {
        println("Hello $name!")
    } else {
        println("Hello guest!")
    }
}

如果我使用 D8 将其转换为 dex,字节码将如下:

[000240] DemoKt.main:()V
0000: const-string v0, "world"
0002: invoke-static {v0}, LDemoKt;.greet:(Ljava/lang/String;)V
0005: return-void

[0001dc] DemoKt.greet:(Ljava/lang/String;)V
0000: if-eqz v2, 0021 // +0021
0002:   new-instance v0, Ljava/lang/StringBuilder;
0004:   invoke-direct {v0}, Ljava/lang/StringBuilder;.<init>:()V
0007:   const-string v1, "Hello "
0009:   invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
000c:   move-result-object v0
000d:   invoke-virtual {v0, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
0010:   move-result-object v0
0011:   const/16 v1, #int 33 // #21
0013:   invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;.append:(C)Ljava/lang/StringBuilder;
0016:   move-result-object v0
0017:   invoke-virtual {v0}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String;
001a:   move-result-object v0
001b:   sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000
001d:   invoke-virtual {v1, v0}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V
0020:   goto 0028 // +0008
0021: const-string v0, "Hello guest!"
0023: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000
0025: invoke-virtual {v1, v0}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V
0028: return-void

我可以看到“guest”常量字符串被加载到寄存器v0中,但是greet函数使用v2进行操作。这是为什么?是否有某种寄存器重新映射?

android bytecode android-runtime
1个回答
0
投票
方法的 N 个参数按顺序放置在方法调用帧的 LAST N 个寄存器中。

https://source.android.com/docs/core/runtime/dalvik-bytecode

您的反汇编输出并未说明为“greet”方法分配了多少个寄存器,但根据结果,必须有三个 - v0、v1 和 v2。

每个堆栈帧都有自己的寄存器;一帧的这些寄存器的内容不会影响其他帧中发生的情况。换句话说,“world”保留在 v0 中的“main”方法中,但放置在 v2 中的“greet”方法中,因为这就是它的工作方式。

© www.soinside.com 2019 - 2024. All rights reserved.