第150集:函数调用的中间代码生成
核心知识点讲解
函数调用的基本形式
在编程语言中,函数调用的基本形式如下:
result = function_name(argument1, argument2, ..., argumentN);其中,function_name 是函数的名称,argument1, argument2, ..., argumentN 是传递给函数的参数,result 是函数的返回值。
函数调用的执行流程
函数调用的执行流程通常包括以下步骤:
参数传递:将实际参数的值传递给函数的形式参数。
保存返回地址:保存当前程序计数器的值,以便函数执行完毕后能够返回到调用点。
跳转到函数入口:跳转到函数的起始地址开始执行函数体。
执行函数体:执行函数体内的语句。
返回值处理:将函数的返回值存储在指定的位置。
返回到调用点:跳回到之前保存的返回地址,继续执行调用点之后的代码。
函数调用的中间代码生成
生成函数调用的中间代码需要考虑以下几个方面:
参数传递:生成参数传递的中间代码,将实际参数的值传递给形式参数。
函数调用指令:生成函数调用的中间代码,跳转到函数入口。
返回值处理:生成返回值处理的中间代码,将函数的返回值存储在指定的位置。
参数传递的方式
参数传递的方式主要有以下几种:
值传递:将实际参数的值复制一份传递给形式参数,函数内部对形式参数的修改不会影响实际参数。
引用传递:将实际参数的地址传递给形式参数,函数内部对形式参数的修改会影响实际参数。
指针传递:将实际参数的指针传递给形式参数,函数内部可以通过指针修改实际参数。
数组传递:在C语言中,数组作为参数传递时,实际上是传递数组的首地址。
结构体传递:结构体作为参数传递时,可以按值传递(复制整个结构体)或按引用传递(传递结构体的地址)。
调用序列(Calling Sequence)
调用序列是指在函数调用过程中,调用者和被调用者之间的职责划分。通常包括以下步骤:
调用者的职责:
- 计算实际参数的值。
- 将实际参数传递给被调用者。
- 保存调用者的上下文(如寄存器值)。
- 跳转到函数入口。
被调用者的职责:
- 保存被调用者的上下文。
- 分配局部变量的空间。
- 执行函数体。
- 保存返回值。
- 恢复被调用者的上下文。
- 返回到调用点。
调用者的后续职责:
- 恢复调用者的上下文。
- 获取返回值。
实用案例分析
案例1:简单函数调用
示例代码
int add(int a, int b) {
return a + b;
}
int main() {
int x = 5;
int y = 10;
int z = add(x, y);
return 0;
}生成的三地址码
// add 函数的中间代码
add:
// 函数序言
// 计算 a + b
t1 = a + b
// 保存返回值
return t1
// 函数尾声
// main 函数的中间代码
main:
// 函数序言
x = 5
y = 10
// 传递参数
param x
param y
// 调用 add 函数
t2 = call add, 2
// 处理返回值
z = t2
return 0
// 函数尾声案例2:带表达式参数的函数调用
示例代码
int max(int a, int b) {
if (a > b) {
return a;
} else {
return b;
}
}
int main() {
int x = 5;
int y = 10;
int z = max(x + 1, y - 2);
return 0;
}生成的三地址码
// max 函数的中间代码
max:
// 函数序言
if a > b goto L1
goto L2
L1: return a
L2: return b
// 函数尾声
// main 函数的中间代码
main:
// 函数序言
x = 5
y = 10
// 计算参数表达式
t1 = x + 1
t2 = y - 2
// 传递参数
param t1
param t2
// 调用 max 函数
t3 = call max, 2
// 处理返回值
z = t3
return 0
// 函数尾声案例3:嵌套函数调用
示例代码
int add(int a, int b) {
return a + b;
}
int mul(int a, int b) {
return a * b;
}
int main() {
int x = 5;
int y = 10;
int z = add(mul(x, 2), y);
return 0;
}生成的三地址码
// add 函数的中间代码
add:
// 函数序言
t1 = a + b
return t1
// 函数尾声
// mul 函数的中间代码
mul:
// 函数序言
t2 = a * b
return t2
// 函数尾声
// main 函数的中间代码
main:
// 函数序言
x = 5
y = 10
// 计算内层函数调用 mul(x, 2)
param x
param 2
t3 = call mul, 2
// 传递参数给外层函数 add
param t3
param y
// 调用 add 函数
t4 = call add, 2
// 处理返回值
z = t4
return 0
// 函数尾声案例4:带数组参数的函数调用
示例代码
int sum(int arr[], int n) {
int total = 0;
for (int i = 0; i < n; i++) {
total += arr[i];
}
return total;
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
int n = 5;
int total = sum(arr, n);
return 0;
}生成的三地址码
// sum 函数的中间代码
sum:
// 函数序言
total = 0
i = 0
L1: if i < n goto L2
goto L3
L2: t1 = i * 4
t2 = arr + t1
t3 = *t2
t4 = total + t3
total = t4
t5 = i + 1
i = t5
goto L1
L3: return total
// 函数尾声
// main 函数的中间代码
main:
// 函数序言
// 初始化数组 arr
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
arr[4] = 5
n = 5
// 传递参数(数组名传递的是首地址)
param arr
param n
// 调用 sum 函数
t6 = call sum, 2
// 处理返回值
total = t6
return 0
// 函数尾声案例5:带结构体参数的函数调用
示例代码
struct Point {
int x;
int y;
};
struct Point add_point(struct Point p1, struct Point p2) {
struct Point result;
result.x = p1.x + p2.x;
result.y = p1.y + p2.y;
return result;
}
int main() {
struct Point p1 = {1, 2};
struct Point p2 = {3, 4};
struct Point p3 = add_point(p1, p2);
return 0;
}生成的三地址码
// add_point 函数的中间代码
add_point:
// 函数序言
// 计算 result.x
t1 = p1.x
t2 = p2.x
t3 = t1 + t2
result.x = t3
// 计算 result.y
t4 = p1.y
t5 = p2.y
t6 = t4 + t5
result.y = t6
// 返回 result
return result
// 函数尾声
// main 函数的中间代码
main:
// 函数序言
// 初始化结构体 p1
p1.x = 1
p1.y = 2
// 初始化结构体 p2
p2.x = 3
p2.y = 4
// 传递参数(按值传递,复制整个结构体)
param p1
param p2
// 调用 add_point 函数
t7 = call add_point, 2
// 处理返回值
p3 = t7
return 0
// 函数尾声代码实现
下面是一个简单的函数调用中间代码生成器的实现,使用 Python 语言:
class FunctionCallCodeGenerator:
def __init__(self):
self.temp_count = 0
self.label_count = 0
self.instructions = []
def new_temp(self):
"""生成新的临时变量名"""
self.temp_count += 1
return f"t{self.temp_count}"
def new_label(self):
"""生成新的标签名"""
self.label_count += 1
return f"L{self.label_count}"
def generate_expression(self, expr):
"""生成表达式的中间代码
Args:
expr: 表达式,以元组形式表示,如 ('+', 'a', 'b')
Returns:
存储表达式结果的临时变量名
"""
if isinstance(expr, tuple):
op = expr[0]
if op == '+' or op == '-' or op == '*' or op == '/':
# 二元运算符
left = self.generate_expression(expr[1])
right = self.generate_expression(expr[2])
temp = self.new_temp()
self.instructions.append(f"{temp} = {left} {op} {right}")
return temp
elif op == '>' or op == '<' or op == '>=' or op == '<=' or op == '==' or op == '!=':
# 比较运算符
left = self.generate_expression(expr[1])
right = self.generate_expression(expr[2])
temp = self.new_temp()
self.instructions.append(f"{temp} = {left} {op} {right}")
return temp
elif op == 'call':
# 函数调用
func_name = expr[1]
args = expr[2]
return self.generate_function_call(func_name, args)
else:
# 常量或变量
return expr
def generate_statement(self, stmt):
"""生成语句的中间代码
Args:
stmt: 语句,可以是赋值语句、if语句等
"""
if isinstance(stmt, dict):
if 'type' in stmt:
if stmt['type'] == 'assignment':
# 赋值语句
lvalue = stmt['lvalue']
rvalue = stmt['rvalue']
result = self.generate_expression(rvalue)
self.instructions.append(f"{lvalue} = {result}")
elif stmt['type'] == 'if':
# if语句
condition = stmt['condition']
then_stmt = stmt['then_stmt']
else_stmt = stmt.get('else_stmt', None)
# 生成条件表达式的中间代码
cond_result = self.generate_expression(condition)
# 生成标签
then_label = self.new_label()
end_label = self.new_label()
# 生成条件跳转指令
self.instructions.append(f"if {cond_result} goto {then_label}")
# 如果有else分支,生成跳转到else标签的指令
if else_stmt:
else_label = self.new_label()
self.instructions.append(f"goto {else_label}")
else:
self.instructions.append(f"goto {end_label}")
# 生成then分支的语句
self.instructions.append(f"{then_label}:")
self.generate_statement(then_stmt)
self.instructions.append(f"goto {end_label}")
# 如果有else分支,生成else分支的语句
if else_stmt:
self.instructions.append(f"{else_label}:")
self.generate_statement(else_stmt)
# 生成结束标签
self.instructions.append(f"{end_label}:")
elif stmt['type'] == 'return':
# return语句
expr = stmt.get('expression', None)
if expr:
result = self.generate_expression(expr)
self.instructions.append(f"return {result}")
else:
self.instructions.append(f"return")
elif stmt['type'] == 'for':
# for循环
init_stmt = stmt['init']
condition = stmt['condition']
update_stmt = stmt['update']
body_stmt = stmt['body']
# 生成初始化语句
self.generate_statement(init_stmt)
# 生成标签
loop_label = self.new_label()
body_label = self.new_label()
end_label = self.new_label()
# 生成循环开始标签
self.instructions.append(f"{loop_label}:")
# 生成条件表达式
cond_result = self.generate_expression(condition)
# 生成条件跳转指令
self.instructions.append(f"if !{cond_result} goto {end_label}")
# 生成循环体开始标签
self.instructions.append(f"{body_label}:")
# 生成循环体语句
self.generate_statement(body_stmt)
# 生成更新语句
self.generate_statement(update_stmt)
# 生成跳转到循环开始的指令
self.instructions.append(f"goto {loop_label}")
# 生成循环结束标签
self.instructions.append(f"{end_label}:")
def generate_function_call(self, func_name, args):
"""生成函数调用的中间代码
Args:
func_name: 函数名
args: 参数列表
Returns:
存储返回值的临时变量名
"""
# 生成参数传递的中间代码
for arg in args:
arg_result = self.generate_expression(arg)
self.instructions.append(f"param {arg_result}")
# 生成函数调用指令
temp = self.new_temp()
self.instructions.append(f"{temp} = call {func_name}, {len(args)}")
return temp
def generate_function_definition(self, func_name, params, body):
"""生成函数定义的中间代码
Args:
func_name: 函数名
params: 参数列表
body: 函数体语句
"""
# 生成函数入口标签
self.instructions.append(f"{func_name}:")
# 生成函数序言
self.instructions.append(" // 函数序言")
# 生成函数体的中间代码
self.generate_statement(body)
# 生成函数尾声
self.instructions.append(" // 函数尾声")
def get_instructions(self):
"""获取生成的中间代码指令"""
return self.instructions
# 测试代码
generator = FunctionCallCodeGenerator()
# 测试函数定义
print("测试函数定义和调用:")
# 生成 add 函数的中间代码
add_func = {
'type': 'return',
'expression': ('+', 'a', 'b')
}
generator.generate_function_definition('add', ['a', 'b'], add_func)
# 生成 main 函数的中间代码
main_func = {
'type': 'assignment',
'lvalue': 'z',
'rvalue': ('call', 'add', ['x', 'y'])
}
generator.generate_function_definition('main', [], main_func)
# 生成调用点的代码
generator.instructions.append("// 调用点的代码")
generator.instructions.append("x = 5")
generator.instructions.append("y = 10")
call_result = generator.generate_function_call('add', ['x', 'y'])
generator.instructions.append(f"z = {call_result}")
# 打印生成的中间代码
for instr in generator.get_instructions():
print(instr)运行结果
测试函数定义和调用:
add:
// 函数序言
t1 = a + b
return t1
// 函数尾声
main:
// 函数序言
t2 = x + y
return t2
// 函数尾声
// 调用点的代码
x = 5
y = 10
param x
param y
t3 = call add, 2
z = t3总结
函数调用的中间代码生成是编译器前端的重要组成部分,它涉及到参数传递、函数调用指令生成和返回值处理等方面。在生成函数调用的中间代码时,需要考虑以下几点:
参数传递:根据参数的类型和传递方式(值传递、引用传递、指针传递等)生成相应的中间代码。
函数调用指令:生成函数调用的中间代码,跳转到函数入口。
返回值处理:生成返回值处理的中间代码,将函数的返回值存储在指定的位置。
调用序列:遵循调用者和被调用者之间的职责划分,确保函数调用的正确执行。
特殊参数处理:对于数组、结构体等特殊类型的参数,需要生成相应的处理代码。
通过正确生成函数调用的中间代码,可以确保编译器能够正确处理函数调用的逻辑,为后续的代码优化和目标代码生成做准备。