cranelift_codegen_meta/cdsl/
instructions.rs1use std::fmt;
2use std::rc::Rc;
3
4use crate::cdsl::camel_case;
5use crate::cdsl::formats::InstructionFormat;
6use crate::cdsl::operands::Operand;
7use crate::cdsl::typevar::TypeVar;
8
9pub(crate) type AllInstructions = Vec<Instruction>;
10
11pub(crate) struct InstructionGroupBuilder<'all_inst> {
12 all_instructions: &'all_inst mut AllInstructions,
13}
14
15impl<'all_inst> InstructionGroupBuilder<'all_inst> {
16 pub fn new(all_instructions: &'all_inst mut AllInstructions) -> Self {
17 Self { all_instructions }
18 }
19
20 pub fn push(&mut self, builder: InstructionBuilder) {
21 let inst = builder.build();
22 self.all_instructions.push(inst);
23 }
24}
25
26#[derive(Debug)]
27pub(crate) struct PolymorphicInfo {
28 pub use_typevar_operand: bool,
29 pub ctrl_typevar: TypeVar,
30}
31
32#[derive(Debug)]
33pub(crate) struct InstructionContent {
34 pub name: String,
36 pub camel_name: String,
37
38 pub doc: String,
40
41 pub operands_in: Vec<Operand>,
43 pub operands_out: Vec<Operand>,
45
46 pub format: Rc<InstructionFormat>,
48
49 pub polymorphic_info: Option<PolymorphicInfo>,
52
53 pub value_opnums: Vec<usize>,
55 pub imm_opnums: Vec<usize>,
57 pub value_results: Vec<usize>,
59
60 pub is_terminator: bool,
62 pub is_branch: bool,
64 pub is_call: bool,
66 pub is_return: bool,
68 pub can_load: bool,
70 pub can_store: bool,
72 pub can_trap: bool,
74 pub other_side_effects: bool,
76 pub side_effects_idempotent: bool,
78}
79
80impl InstructionContent {
81 pub fn snake_name(&self) -> &str {
82 if &self.name == "return" {
83 "return_"
84 } else {
85 &self.name
86 }
87 }
88}
89
90pub(crate) type Instruction = Rc<InstructionContent>;
91
92impl fmt::Display for InstructionContent {
93 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
94 if !self.operands_out.is_empty() {
95 let operands_out = self
96 .operands_out
97 .iter()
98 .map(|op| op.name)
99 .collect::<Vec<_>>()
100 .join(", ");
101 fmt.write_str(&operands_out)?;
102 fmt.write_str(" = ")?;
103 }
104
105 fmt.write_str(&self.name)?;
106
107 if !self.operands_in.is_empty() {
108 let operands_in = self
109 .operands_in
110 .iter()
111 .map(|op| op.name)
112 .collect::<Vec<_>>()
113 .join(", ");
114 fmt.write_str(" ")?;
115 fmt.write_str(&operands_in)?;
116 }
117
118 Ok(())
119 }
120}
121
122pub(crate) struct InstructionBuilder {
123 name: String,
124 doc: String,
125 format: Rc<InstructionFormat>,
126 operands_in: Option<Vec<Operand>>,
127 operands_out: Option<Vec<Operand>>,
128
129 is_terminator: bool,
131 is_branch: bool,
132 is_call: bool,
133 is_return: bool,
134 can_load: bool,
135 can_store: bool,
136 can_trap: bool,
137 other_side_effects: bool,
138 side_effects_idempotent: bool,
139}
140
141impl InstructionBuilder {
142 pub fn new<S: Into<String>>(name: S, doc: S, format: &Rc<InstructionFormat>) -> Self {
143 Self {
144 name: name.into(),
145 doc: doc.into(),
146 format: format.clone(),
147 operands_in: None,
148 operands_out: None,
149
150 is_terminator: false,
151 is_branch: false,
152 is_call: false,
153 is_return: false,
154 can_load: false,
155 can_store: false,
156 can_trap: false,
157 other_side_effects: false,
158 side_effects_idempotent: false,
159 }
160 }
161
162 pub fn operands_in(mut self, operands: Vec<Operand>) -> Self {
163 assert!(self.operands_in.is_none());
164 self.operands_in = Some(operands);
165 self
166 }
167
168 pub fn operands_out(mut self, operands: Vec<Operand>) -> Self {
169 assert!(self.operands_out.is_none());
170 self.operands_out = Some(operands);
171 self
172 }
173
174 pub fn terminates_block(mut self) -> Self {
176 self.is_terminator = true;
177 self
178 }
179
180 pub fn branches(mut self) -> Self {
183 self.is_branch = true;
184 self.terminates_block()
185 }
186
187 pub fn call(mut self) -> Self {
189 self.is_call = true;
190 self
191 }
192
193 pub fn returns(mut self) -> Self {
196 self.is_return = true;
197 self.terminates_block()
198 }
199
200 pub fn can_load(mut self) -> Self {
202 self.can_load = true;
203 self
204 }
205
206 pub fn can_store(mut self) -> Self {
208 self.can_store = true;
209 self
210 }
211
212 pub fn can_trap(mut self) -> Self {
214 self.can_trap = true;
215 self
216 }
217
218 pub fn other_side_effects(mut self) -> Self {
220 self.other_side_effects = true;
221 self
222 }
223
224 pub fn side_effects_idempotent(mut self) -> Self {
226 self.side_effects_idempotent = true;
227 self
228 }
229
230 fn build(self) -> Instruction {
231 let operands_in = self.operands_in.unwrap_or_default();
232 let operands_out = self.operands_out.unwrap_or_default();
233
234 let mut value_opnums = Vec::new();
235 let mut imm_opnums = Vec::new();
236 for (i, op) in operands_in.iter().enumerate() {
237 if op.is_value() {
238 value_opnums.push(i);
239 } else if op.is_immediate_or_entityref() {
240 imm_opnums.push(i);
241 } else {
242 assert!(op.is_varargs());
243 }
244 }
245
246 let value_results = operands_out
247 .iter()
248 .enumerate()
249 .filter_map(|(i, op)| if op.is_value() { Some(i) } else { None })
250 .collect();
251
252 verify_format(&self.name, &operands_in, &self.format);
253
254 let polymorphic_info =
255 verify_polymorphic(&operands_in, &operands_out, &self.format, &value_opnums);
256
257 let camel_name = camel_case(&self.name);
258
259 Rc::new(InstructionContent {
260 name: self.name,
261 camel_name,
262 doc: self.doc,
263 operands_in,
264 operands_out,
265 format: self.format,
266 polymorphic_info,
267 value_opnums,
268 value_results,
269 imm_opnums,
270 is_terminator: self.is_terminator,
271 is_branch: self.is_branch,
272 is_call: self.is_call,
273 is_return: self.is_return,
274 can_load: self.can_load,
275 can_store: self.can_store,
276 can_trap: self.can_trap,
277 other_side_effects: self.other_side_effects,
278 side_effects_idempotent: self.side_effects_idempotent,
279 })
280 }
281}
282
283fn verify_format(inst_name: &str, operands_in: &[Operand], format: &InstructionFormat) {
285 let mut num_values = 0;
290 let mut num_blocks = 0;
291 let mut num_immediates = 0;
292
293 for operand in operands_in.iter() {
294 if operand.is_varargs() {
295 assert!(
296 format.has_value_list,
297 "instruction {} has varargs, but its format {} doesn't have a value list; you may \
298 need to use a different format.",
299 inst_name, format.name
300 );
301 }
302 if operand.is_value() {
303 num_values += 1;
304 }
305 if operand.kind.is_block() {
306 num_blocks += 1;
307 } else if operand.is_immediate_or_entityref() {
308 if let Some(format_field) = format.imm_fields.get(num_immediates) {
309 assert_eq!(
310 format_field.kind.rust_field_name,
311 operand.kind.rust_field_name,
312 "{}th operand of {} should be {} (according to format), not {} (according to \
313 inst definition). You may need to use a different format.",
314 num_immediates,
315 inst_name,
316 format_field.kind.rust_field_name,
317 operand.kind.rust_field_name
318 );
319 num_immediates += 1;
320 }
321 }
322 }
323
324 assert_eq!(
325 num_values, format.num_value_operands,
326 "inst {} doesn't have as many value input operands as its format {} declares; you may need \
327 to use a different format.",
328 inst_name, format.name
329 );
330
331 assert_eq!(
332 num_blocks, format.num_block_operands,
333 "inst {} doesn't have as many block input operands as its format {} declares; you may need \
334 to use a different format.",
335 inst_name, format.name,
336 );
337
338 assert_eq!(
339 num_immediates,
340 format.imm_fields.len(),
341 "inst {} doesn't have as many immediate input \
342 operands as its format {} declares; you may need to use a different format.",
343 inst_name,
344 format.name
345 );
346}
347
348fn verify_polymorphic(
350 operands_in: &[Operand],
351 operands_out: &[Operand],
352 format: &InstructionFormat,
353 value_opnums: &[usize],
354) -> Option<PolymorphicInfo> {
355 let is_polymorphic = operands_in
357 .iter()
358 .any(|op| op.is_value() && op.type_var().unwrap().free_typevar().is_some())
359 || operands_out
360 .iter()
361 .any(|op| op.is_value() && op.type_var().unwrap().free_typevar().is_some());
362
363 if !is_polymorphic {
364 return None;
365 }
366
367 let tv_op = format.typevar_operand;
369 let mut maybe_error_message = None;
370 if let Some(tv_op) = tv_op {
371 if tv_op < value_opnums.len() {
372 let op_num = value_opnums[tv_op];
373 let tv = operands_in[op_num].type_var().unwrap();
374 let free_typevar = tv.free_typevar();
375 if (free_typevar.is_some() && tv == &free_typevar.unwrap())
376 || tv.singleton_type().is_some()
377 {
378 match is_ctrl_typevar_candidate(tv, operands_in, operands_out) {
379 Ok(_other_typevars) => {
380 return Some(PolymorphicInfo {
381 use_typevar_operand: true,
382 ctrl_typevar: tv.clone(),
383 });
384 }
385 Err(error_message) => {
386 maybe_error_message = Some(error_message);
387 }
388 }
389 }
390 }
391 };
392
393 if operands_out.is_empty() {
397 match maybe_error_message {
399 Some(msg) => panic!("{}", msg),
400 None => panic!("typevar_operand must be a free type variable"),
401 }
402 }
403
404 let tv = operands_out[0].type_var().unwrap();
406 let free_typevar = tv.free_typevar();
407 if free_typevar.is_some() && tv != &free_typevar.unwrap() {
408 panic!("first result must be a free type variable");
409 }
410
411 is_ctrl_typevar_candidate(tv, operands_in, operands_out).unwrap();
414
415 Some(PolymorphicInfo {
416 use_typevar_operand: false,
417 ctrl_typevar: tv.clone(),
418 })
419}
420
421fn is_ctrl_typevar_candidate(
431 ctrl_typevar: &TypeVar,
432 operands_in: &[Operand],
433 operands_out: &[Operand],
434) -> Result<Vec<TypeVar>, String> {
435 let mut other_typevars = Vec::new();
436
437 for input in operands_in {
439 if !input.is_value() {
440 continue;
441 }
442
443 let typ = input.type_var().unwrap();
444 let free_typevar = typ.free_typevar();
445
446 if free_typevar.is_none() {
448 continue;
449 }
450 let free_typevar = free_typevar.unwrap();
451 if &free_typevar == ctrl_typevar {
452 continue;
453 }
454
455 if typ != &free_typevar {
457 return Err(format!(
458 "{:?}: type variable {} must be derived from {:?} while it is derived from {:?}",
459 input, typ.name, ctrl_typevar, free_typevar
460 ));
461 }
462
463 for other_tv in &other_typevars {
465 if &free_typevar == other_tv {
466 return Err(format!(
467 "non-controlling type variable {} can't be used more than once",
468 free_typevar.name
469 ));
470 }
471 }
472
473 other_typevars.push(free_typevar);
474 }
475
476 for result in operands_out {
478 if !result.is_value() {
479 continue;
480 }
481
482 let typ = result.type_var().unwrap();
483 let free_typevar = typ.free_typevar();
484
485 if free_typevar.is_none() || &free_typevar.unwrap() == ctrl_typevar {
487 continue;
488 }
489
490 return Err("type variable in output not derived from ctrl_typevar".into());
491 }
492
493 Ok(other_typevars)
494}