具有绝对返回路径的方法是如何内联的
本文关键字:方法 何内联 返回路径 | 更新日期: 2023-09-27 18:29:35
我主要使用C#进行开发,但我认为这个问题可能也适用于其他语言
此外,这里似乎有很多代码,但问题很简单
据我所知,内联是编译器(在C#虚拟机的情况下)通过在调用方法的每个地方插入方法体来替换方法调用。
假设我有以下程序:
static Main()
{
int number = 7;
bool a;
a = IsEven(number);
Console.WriteLine(a);
}
方法主体IsEven
:
bool IsEven(int n)
{
if (n % 2 == 0) // Two conditional return paths
return true;
else
return false;
}
我可以理解内联方法后代码的样子:
static Main()
{
int number = 7;
bool a;
if (number % 2 == 0)
a = true;
else
a = false;
Console.WriteLine(a); // Will print true if 'number' is even, otherwise false
}
一个明显简单而正确的程序。
但是,如果我稍微调整一下IsEven
的主体,使其包含一个绝对返回路径。。。
bool IsEven(int n)
{
if (n % 2 == 0)
return true;
return false; // <- Absolute return path!
}
在某些情况下,我个人更喜欢这种风格。一些折射工具甚至可能建议我把第一个版本改成这样——但当我试图想象这个方法内联时会是什么样子时,我被难住了
如果我们内联方法的第二个版本:
static Main()
{
int number = 7;
bool a;
if (number % 2 == 0)
a = true;
a = false;
Console.WriteLine(a); // Will always print false!
}
要问的问题:
编译器/虚拟机如何处理具有绝对返回路径的方法的内联
像这样的事情似乎不太可能真正阻止方法内联,所以我想知道如何处理这些事情。也许内联的过程并没有这么简单?也许某个版本更有可能被VM内联?
编辑:分析这两个方法(以及第一个方法的手动内联)没有显示出性能上的差异,所以我只能假设两个方法都是内联的,并且以相同或相似的方式工作(至少在我的VM上)
此外,这些方法非常简单,似乎几乎可以互换,但具有绝对返回路径的复杂方法可能更难更改为没有绝对返回路径。
很难解释JITter在内联时会做什么-它不会改变C#代码来进行内联-它将(总是?)处理生成的字节(编译版本)-并且在生成汇编代码(实际的机器代码字节)时使用的"工具"比C#(或IL)中使用的"更细粒度"。
也就是说,您可以通过考虑break
关键字来了解它是如何工作的,用C#术语来说。。
考虑这样一种可能性,即每个不平凡的内联函数都包含在while (true)
循环(或do while(false)
循环)中,并且每个源返回都被转换为localVar = result; break;
语句集。然后你会得到这样的东西:
static Main()
{
int number = 7;
bool a;
while (true)
{
if (number % 2 == 0)
{
a = true;
break;
}
a = false;
break;
}
Console.WriteLine(a); // Will always print the right thing! Yey!
}
类似地,在生成汇编时,你会看到生成了很多jmp
——这些在道德上相当于break语句,但它们更灵活(可以把它们想象成匿名goto之类的)。
所以你可以看到,抖动(以及任何编译为本机的编译器)手头有很多工具可以用来做"正确的事情"。
return
语句指示:
- 结果值
-
局部变量的销毁(这适用于C++,而不是C#)在C#中,finally
块和using
块中的Dispose调用将运行 - 跳出函数
所有这些事情在内联之后仍然会发生。内联后,跳转将是本地的,而不是跨函数的,但它仍然存在。
内嵌不是像C/C++宏那样的文本替换。
内联和文本替换之间的其他不同之处在于对同名变量的处理。
cpu执行的机器代码是一种非常简单的语言。它没有return语句的概念,子程序有一个入口点和一个出口点。所以你的IsEven()方法是这样的:
bool IsEven(int n)
{
if (n % 2 == 0)
return true;
return false;
}
需要通过抖动重写为类似的东西(无效C#):
void IsEvent(int n)
{
if (n % 2 == 0) {
$retval = true;
goto exit;
}
$retval = false;
exit:
} // $retval becomes the function return value
$retval变量在这里看起来可能是假的。它不是,它是x86内核上的EAX寄存器。您现在将看到,此代码易于内联,可以直接移植到Main()的主体中。$retval变量可以通过简单的逻辑替换等同于a
变量。