第150集:函数调用的中间代码生成

核心知识点讲解

函数调用的基本形式

在编程语言中,函数调用的基本形式如下:

result = function_name(argument1, argument2, ..., argumentN);

其中,function_name 是函数的名称,argument1, argument2, ..., argumentN 是传递给函数的参数,result 是函数的返回值。

函数调用的执行流程

函数调用的执行流程通常包括以下步骤:

  1. 参数传递:将实际参数的值传递给函数的形式参数。

  2. 保存返回地址:保存当前程序计数器的值,以便函数执行完毕后能够返回到调用点。

  3. 跳转到函数入口:跳转到函数的起始地址开始执行函数体。

  4. 执行函数体:执行函数体内的语句。

  5. 返回值处理:将函数的返回值存储在指定的位置。

  6. 返回到调用点:跳回到之前保存的返回地址,继续执行调用点之后的代码。

函数调用的中间代码生成

生成函数调用的中间代码需要考虑以下几个方面:

  1. 参数传递:生成参数传递的中间代码,将实际参数的值传递给形式参数。

  2. 函数调用指令:生成函数调用的中间代码,跳转到函数入口。

  3. 返回值处理:生成返回值处理的中间代码,将函数的返回值存储在指定的位置。

参数传递的方式

参数传递的方式主要有以下几种:

  1. 值传递:将实际参数的值复制一份传递给形式参数,函数内部对形式参数的修改不会影响实际参数。

  2. 引用传递:将实际参数的地址传递给形式参数,函数内部对形式参数的修改会影响实际参数。

  3. 指针传递:将实际参数的指针传递给形式参数,函数内部可以通过指针修改实际参数。

  4. 数组传递:在C语言中,数组作为参数传递时,实际上是传递数组的首地址。

  5. 结构体传递:结构体作为参数传递时,可以按值传递(复制整个结构体)或按引用传递(传递结构体的地址)。

调用序列(Calling Sequence)

调用序列是指在函数调用过程中,调用者和被调用者之间的职责划分。通常包括以下步骤:

  1. 调用者的职责

    • 计算实际参数的值。
    • 将实际参数传递给被调用者。
    • 保存调用者的上下文(如寄存器值)。
    • 跳转到函数入口。
  2. 被调用者的职责

    • 保存被调用者的上下文。
    • 分配局部变量的空间。
    • 执行函数体。
    • 保存返回值。
    • 恢复被调用者的上下文。
    • 返回到调用点。
  3. 调用者的后续职责

    • 恢复调用者的上下文。
    • 获取返回值。

实用案例分析

案例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

总结

函数调用的中间代码生成是编译器前端的重要组成部分,它涉及到参数传递、函数调用指令生成和返回值处理等方面。在生成函数调用的中间代码时,需要考虑以下几点:

  1. 参数传递:根据参数的类型和传递方式(值传递、引用传递、指针传递等)生成相应的中间代码。

  2. 函数调用指令:生成函数调用的中间代码,跳转到函数入口。

  3. 返回值处理:生成返回值处理的中间代码,将函数的返回值存储在指定的位置。

  4. 调用序列:遵循调用者和被调用者之间的职责划分,确保函数调用的正确执行。

  5. 特殊参数处理:对于数组、结构体等特殊类型的参数,需要生成相应的处理代码。

通过正确生成函数调用的中间代码,可以确保编译器能够正确处理函数调用的逻辑,为后续的代码优化和目标代码生成做准备。

« 上一篇 开关语句的中间代码生成 下一篇 » 函数定义的中间代码生成