luaur_code_gen/functions/
try_create_linear_block.rs1use crate::enums::ir_block_kind::IrBlockKind;
2use crate::enums::ir_cmd::IrCmd;
3use crate::enums::ir_op_kind::IrOpKind;
4use crate::functions::collect_direct_block_jump_path::collect_direct_block_jump_path;
5use crate::functions::const_prop_in_block::const_prop_in_block;
6use crate::functions::replace_ir_utils::replace_ir_function_ir_op_ir_op;
7use crate::functions::setup_block_entry_state_optimize_const_prop::setup_block_entry_state_ir_builder_ir_function_ir_block_const_prop_state;
8use crate::macros::op_a::op_a;
9use crate::records::const_prop_state::ConstPropState;
10use crate::records::ir_block::IrBlock;
11use crate::records::ir_builder::IrBuilder;
12use crate::records::ir_function::IrFunction;
13use alloc::vec::Vec;
14
15pub fn try_create_linear_block(
16 build: &mut IrBuilder,
17 visited: &mut Vec<u8>,
18 starting_block: *mut IrBlock,
19 state: &mut ConstPropState,
20) {
21 let function: *mut IrFunction = &mut build.function;
22
23 unsafe {
24 let block_idx = (&*function).get_block_index(&*starting_block);
25 crate::macros::codegen_assert::CODEGEN_ASSERT!(visited[block_idx as usize] == 0);
26 visited[block_idx as usize] = 1;
27
28 let term_inst_idx = (*starting_block).finish;
29 let term_inst = (&mut (*function).instructions)[term_inst_idx as usize].clone();
30
31 if term_inst.cmd != IrCmd::JUMP {
32 return;
33 }
34
35 let target_op = op_a(&mut term_inst.clone());
36 if target_op.kind() != IrOpKind::Block {
37 return;
38 }
39
40 if (&(*function).blocks)[target_op.index() as usize].use_count == 1 {
41 return;
42 }
43
44 let target_block_idx = target_op.index();
45 let path = collect_direct_block_jump_path(&mut *function, visited, starting_block);
46
47 if (path.len() as i32) < luaur_common::FInt::LuauCodeGenMinLinearBlockPath.get() {
48 return;
49 }
50
51 state.clear();
52
53 if luaur_common::FFlag::LuauCodegenLinearSetupEntryState3.get() {
54 setup_block_entry_state_ir_builder_ir_function_ir_block_const_prop_state(
55 build,
56 &mut *function,
57 &*starting_block,
58 state,
59 );
60 }
61
62 const_prop_in_block(build, &mut *starting_block, state);
63
64 if luaur_common::FFlag::LuauCodegenLinearSetupEntryState3.get() {
65 let current_target = {
66 let inst = &mut (&mut (*function).instructions)[term_inst_idx as usize].clone();
67 op_a(inst).index()
68 };
69
70 if (*starting_block).finish != term_inst_idx || current_target != target_block_idx {
71 return;
72 }
73
74 if (&(*function).blocks)[target_block_idx as usize].use_count == 1 {
75 return;
76 }
77 } else {
78 let current_target = {
79 let inst =
80 &mut (&mut (*function).instructions)[(*starting_block).finish as usize].clone();
81 op_a(inst).index()
82 };
83
84 if current_target != target_block_idx {
85 crate::macros::codegen_assert::CODEGEN_ASSERT!(false);
86 return;
87 }
88 }
89
90 let starting_sort_key = (*starting_block).sortkey;
91 let starting_chain_key = (*starting_block).chainkey;
92
93 let new_block = build.block(IrBlockKind::Linearized);
94 visited.push(0);
95
96 build.begin_block(new_block);
97
98 build.function.blocks[new_block.index() as usize].sortkey = starting_sort_key;
99 build.function.blocks[new_block.index() as usize].chainkey = starting_chain_key + 1;
100 build.function.blocks[block_idx as usize].expected_next_block = new_block.index();
101
102 let op_ptr = {
103 let inst = &mut build.function.instructions[term_inst_idx as usize];
104 &mut inst.ops.as_mut_slice()[0] as *mut _
105 };
106 replace_ir_function_ir_op_ir_op(&mut build.function, &mut *op_ptr, new_block);
107
108 build.clone(path.clone(), true);
109
110 if build.function.cfg.r#in.len() == new_block.index() as usize {
111 crate::macros::codegen_assert::CODEGEN_ASSERT!(
112 build.function.cfg.r#in.len() == build.function.cfg.out.len()
113 );
114 crate::macros::codegen_assert::CODEGEN_ASSERT!(
115 build.function.cfg.r#in.len() == build.function.cfg.def.len()
116 );
117
118 build
119 .function
120 .cfg
121 .r#in
122 .push(build.function.cfg.r#in[path[0] as usize]);
123 build
124 .function
125 .cfg
126 .out
127 .push(build.function.cfg.out[*path.last().unwrap() as usize]);
128 build.function.cfg.def.push(Default::default());
129
130 let out_vararg_seq = build.function.cfg.out.last().unwrap().vararg_seq;
131 let def_idx = build.function.cfg.def.len() - 1;
132
133 for &path_block_idx in &path {
134 let path_def = build.function.cfg.def[path_block_idx as usize];
135 for i in 0..build.function.cfg.def[def_idx].regs.len() {
136 build.function.cfg.def[def_idx].regs[i] |= path_def.regs[i];
137 }
138
139 if path_def.vararg_seq && out_vararg_seq {
140 build.function.cfg.def[def_idx].vararg_seq = true;
141 build.function.cfg.def[def_idx].vararg_start = path_def.vararg_start;
142 }
143 }
144
145 build
146 .function
147 .cfg
148 .predecessors_offsets
149 .push(build.function.cfg.predecessors.len() as u32);
150 build.function.cfg.predecessors.push(block_idx);
151 }
152
153 let linear_block = &mut build.function.blocks[new_block.index() as usize] as *mut IrBlock;
154 const_prop_in_block(build, &mut *linear_block, state);
155 }
156}