(
fn: FuncIR, emitter: Emitter, source_path: str, module_name: str
)
| 122 | |
| 123 | |
| 124 | def generate_native_function( |
| 125 | fn: FuncIR, emitter: Emitter, source_path: str, module_name: str |
| 126 | ) -> None: |
| 127 | declarations = Emitter(emitter.context) |
| 128 | names = generate_names_for_ir(fn.arg_regs, fn.blocks) |
| 129 | body = Emitter(emitter.context, names) |
| 130 | visitor = FunctionEmitterVisitor(body, declarations, source_path, module_name) |
| 131 | |
| 132 | declarations.emit_line(f"{native_function_header(fn.decl, emitter)} {{") |
| 133 | body.indent() |
| 134 | |
| 135 | for r in all_values(fn.arg_regs, fn.blocks): |
| 136 | if isinstance(r.type, RTuple): |
| 137 | emitter.declare_tuple_struct(r.type) |
| 138 | if isinstance(r.type, RArray): |
| 139 | continue # Special: declared on first assignment |
| 140 | |
| 141 | if r in fn.arg_regs: |
| 142 | continue # Skip the arguments |
| 143 | |
| 144 | ctype = emitter.ctype_spaced(r.type) |
| 145 | init = "" |
| 146 | declarations.emit_line( |
| 147 | "{ctype}{prefix}{name}{init};".format( |
| 148 | ctype=ctype, prefix=REG_PREFIX, name=names[r], init=init |
| 149 | ) |
| 150 | ) |
| 151 | |
| 152 | # Before we emit the blocks, give them all labels |
| 153 | blocks = fn.blocks |
| 154 | for i, block in enumerate(blocks): |
| 155 | block.label = i |
| 156 | |
| 157 | # Find blocks that are never jumped to or are only jumped to from the |
| 158 | # block directly above it. This allows for more labels and gotos to be |
| 159 | # eliminated during code generation. |
| 160 | for block in fn.blocks: |
| 161 | terminator = block.terminator |
| 162 | assert isinstance(terminator, ControlOp), terminator |
| 163 | |
| 164 | for target in terminator.targets(): |
| 165 | is_next_block = target.label == block.label + 1 |
| 166 | |
| 167 | # Always emit labels for GetAttr error checks since the emit code that |
| 168 | # generates them will add instructions between the branch and the |
| 169 | # next label, causing the label to be wrongly removed. A better |
| 170 | # solution would be to change the IR so that it adds a basic block |
| 171 | # in between the calls. |
| 172 | is_problematic_op = isinstance(terminator, Branch) and any( |
| 173 | isinstance(s, GetAttr) for s in terminator.sources() |
| 174 | ) |
| 175 | |
| 176 | if not is_next_block or is_problematic_op: |
| 177 | fn.blocks[target.label].referenced = True |
| 178 | |
| 179 | common = frequently_executed_blocks(fn.blocks[0]) |
| 180 | |
| 181 | for i in range(len(blocks)): |
searching dependent graphs…