GitLab will be upgraded on 30 Jan 2023 from 2.00 pm (AEDT) to 3.00 pm (AEDT). During the update, GitLab and Mattermost services will not be available. If you have any concerns with this, please talk to us at N110 (b) CSIT building.

ret_sink.rs 4.78 KB
Newer Older
qinsoon's avatar
qinsoon committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Copyright 2017 The Australian National University
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use ast::ir::*;
use ast::inst::*;
use ast::ptr::*;
use vm::VM;
use compiler::CompilerPass;
20
use compiler::EPILOGUE_BLOCK_NAME;
qinsoon's avatar
qinsoon committed
21
22
23
24
25
26
27
use std::any::Any;

/// Mu IR the client gives us may contain several RET instructions. However,
/// internally we want a single exit point for a function. In this pass, we
/// create a return sink (a block), and rewrite all the RET instruction into
/// a BRANCH with return values.
pub struct RetSink {
28
    name: &'static str
qinsoon's avatar
qinsoon committed
29
30
31
32
33
}

impl RetSink {
    pub fn new() -> RetSink {
        RetSink {
34
            name: "Creating Return Sink"
qinsoon's avatar
qinsoon committed
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
        }
    }
}

impl CompilerPass for RetSink {
    fn name(&self) -> &'static str {
        self.name
    }

    fn as_any(&self) -> &Any {
        self
    }

    fn visit_function(&mut self, vm: &VM, func: &mut MuFunctionVersion) {
        let mut f_content = func.content.take().unwrap();

        // create a return sink
        let return_sink = {
53
            let block_name = Arc::new(format!("{}:{}", func.name(), EPILOGUE_BLOCK_NAME));
qinsoon's avatar
qinsoon committed
54
55
56
            trace!("created return sink {}", block_name);

            let mut block = Block::new(MuEntityHeader::named(vm.next_id(), block_name));
57
58
            // tell the compiler this is the return sink
            block.trace_hint = TraceHint::ReturnSink;
qinsoon's avatar
qinsoon committed
59
60
61
            vm.set_name(block.as_entity());

            let sig = func.sig.clone();
qinsoon's avatar
qinsoon committed
62
63
64
65
66
67
68
            let args: Vec<P<Value>> = sig.ret_tys
                .iter()
                .map(|ty| {
                    func.new_ssa(MuEntityHeader::unnamed(vm.next_id()), ty.clone())
                        .clone_value()
                })
                .collect();
qinsoon's avatar
qinsoon committed
69
70
71
72
73
74
75
76

            block.content = Some(BlockContent {
                args: args.clone(),
                exn_arg: None,
                body: vec![
                    func.new_inst(Instruction {
                        hdr: MuEntityHeader::unnamed(vm.next_id()),
                        value: None,
qinsoon's avatar
qinsoon committed
77
78
79
                        ops: args.iter()
                            .map(|val| TreeNode::new_value(val.clone()))
                            .collect(),
80
                        v: Instruction_::Return((0..args.len()).collect())
qinsoon's avatar
qinsoon committed
81
                    }),
qinsoon's avatar
qinsoon committed
82
                ],
83
                keepalives: None
qinsoon's avatar
qinsoon committed
84
85
86
87
88
89
90
            });

            block
        };

        // rewrite existing RET instruction to a BRANCH
        // use RET values as BRANCH's goto values
qinsoon's avatar
qinsoon committed
91
        let mut has_ret: bool = false;
qinsoon's avatar
qinsoon committed
92
93
94
95
96
97
98
99
100
        for (blk_id, mut block) in f_content.blocks.iter_mut() {
            trace!("block: {}", blk_id);

            // old block content
            let block_content = block.content.as_ref().unwrap().clone();

            let mut new_body = vec![];

            for node in block_content.body.iter() {
101
                trace!("{}", node);
qinsoon's avatar
qinsoon committed
102
                match node.v {
qinsoon's avatar
qinsoon committed
103
104
105
106
107
                    TreeNode_::Instruction(Instruction {
                        ref ops,
                        v: Instruction_::Return(ref arg_index),
                        ..
                    }) => {
qinsoon's avatar
qinsoon committed
108
109
110
111
112
113
                        let branch_to_sink = func.new_inst(Instruction {
                            hdr: MuEntityHeader::unnamed(vm.next_id()),
                            value: None,
                            ops: ops.clone(),
                            v: Instruction_::Branch1(Destination {
                                target: return_sink.id(),
114
115
                                args: arg_index.iter().map(|i| DestArg::Normal(*i)).collect()
                            })
qinsoon's avatar
qinsoon committed
116
                        });
117
                        trace!(">> rewrite ret to {}", branch_to_sink);
qinsoon's avatar
qinsoon committed
118
                        new_body.push(branch_to_sink);
119
                        has_ret = true;
qinsoon's avatar
qinsoon committed
120
                    }
121
                    _ => new_body.push(node.clone())
qinsoon's avatar
qinsoon committed
122
123
124
125
                }
            }

            block.content = Some(BlockContent {
qinsoon's avatar
qinsoon committed
126
127
128
                args: block_content.args.to_vec(),
                exn_arg: block_content.exn_arg.clone(),
                body: new_body,
129
                keepalives: block_content.keepalives.clone()
qinsoon's avatar
qinsoon committed
130
131
132
133
            });
        }

        // insert return sink
134
135
136
        if has_ret {
            f_content.blocks.insert(return_sink.id(), return_sink);
        }
qinsoon's avatar
qinsoon committed
137
138
139
140

        // put back the function content
        func.content = Some(f_content);
    }
qinsoon's avatar
qinsoon committed
141
}