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 pub inst_builder_imm_method: bool,
83}
84
85impl InstructionContent {
86 pub fn snake_name(&self) -> &str {
87 if &self.name == "return" {
88 "return_"
89 } else {
90 &self.name
91 }
92 }
93}
94
95pub(crate) type Instruction = Rc<InstructionContent>;
96
97impl fmt::Display for InstructionContent {
98 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
99 if !self.operands_out.is_empty() {
100 let operands_out = self
101 .operands_out
102 .iter()
103 .map(|op| op.name)
104 .collect::<Vec<_>>()
105 .join(", ");
106 fmt.write_str(&operands_out)?;
107 fmt.write_str(" = ")?;
108 }
109
110 fmt.write_str(&self.name)?;
111
112 if !self.operands_in.is_empty() {
113 let operands_in = self
114 .operands_in
115 .iter()
116 .map(|op| op.name)
117 .collect::<Vec<_>>()
118 .join(", ");
119 fmt.write_str(" ")?;
120 fmt.write_str(&operands_in)?;
121 }
122
123 Ok(())
124 }
125}
126
127pub(crate) struct InstructionBuilder {
128 name: String,
129 doc: String,
130 format: Rc<InstructionFormat>,
131 operands_in: Option<Vec<Operand>>,
132 operands_out: Option<Vec<Operand>>,
133
134 is_terminator: bool,
136 is_branch: bool,
137 is_call: bool,
138 is_return: bool,
139 can_load: bool,
140 can_store: bool,
141 can_trap: bool,
142 other_side_effects: bool,
143 side_effects_idempotent: bool,
144 inst_builder_imm_method: bool,
145}
146
147impl InstructionBuilder {
148 pub fn new<S: Into<String>>(name: S, doc: S, format: &Rc<InstructionFormat>) -> Self {
149 Self {
150 name: name.into(),
151 doc: doc.into(),
152 format: format.clone(),
153 operands_in: None,
154 operands_out: None,
155
156 is_terminator: false,
157 is_branch: false,
158 is_call: false,
159 is_return: false,
160 can_load: false,
161 can_store: false,
162 can_trap: false,
163 other_side_effects: false,
164 side_effects_idempotent: false,
165 inst_builder_imm_method: false,
166 }
167 }
168
169 pub fn operands_in(mut self, operands: Vec<Operand>) -> Self {
170 assert!(self.operands_in.is_none());
171 self.operands_in = Some(operands);
172 self
173 }
174
175 pub fn operands_out(mut self, operands: Vec<Operand>) -> Self {
176 assert!(self.operands_out.is_none());
177 self.operands_out = Some(operands);
178 self
179 }
180
181 pub fn terminates_block(mut self) -> Self {
183 self.is_terminator = true;
184 self
185 }
186
187 pub fn branches(mut self) -> Self {
190 self.is_branch = true;
191 self.terminates_block()
192 }
193
194 pub fn call(mut self) -> Self {
196 self.is_call = true;
197 self
198 }
199
200 pub fn returns(mut self) -> Self {
203 self.is_return = true;
204 self.terminates_block()
205 }
206
207 pub fn can_load(mut self) -> Self {
209 self.can_load = true;
210 self
211 }
212
213 pub fn can_store(mut self) -> Self {
215 self.can_store = true;
216 self
217 }
218
219 pub fn can_trap(mut self) -> Self {
221 self.can_trap = true;
222 self
223 }
224
225 pub fn other_side_effects(mut self) -> Self {
227 self.other_side_effects = true;
228 self
229 }
230
231 pub fn side_effects_idempotent(mut self) -> Self {
233 self.side_effects_idempotent = true;
234 self
235 }
236
237 pub fn inst_builder_imm_method(mut self, enabled: bool) -> Self {
241 self.inst_builder_imm_method = enabled;
242 self
243 }
244
245 fn build(self) -> Instruction {
246 let operands_in = self.operands_in.unwrap_or_default();
247 let operands_out = self.operands_out.unwrap_or_default();
248
249 let mut value_opnums = Vec::new();
250 let mut imm_opnums = Vec::new();
251 for (i, op) in operands_in.iter().enumerate() {
252 if op.is_value() {
253 value_opnums.push(i);
254 } else if op.is_immediate_or_entityref() {
255 imm_opnums.push(i);
256 } else {
257 assert!(op.is_varargs());
258 }
259 }
260
261 let value_results = operands_out
262 .iter()
263 .enumerate()
264 .filter_map(|(i, op)| if op.is_value() { Some(i) } else { None })
265 .collect();
266
267 verify_format(&self.name, &operands_in, &self.format);
268
269 let polymorphic_info =
270 verify_polymorphic(&operands_in, &operands_out, &self.format, &value_opnums);
271
272 let camel_name = camel_case(&self.name);
273
274 Rc::new(InstructionContent {
275 name: self.name,
276 camel_name,
277 doc: self.doc,
278 operands_in,
279 operands_out,
280 format: self.format,
281 polymorphic_info,
282 value_opnums,
283 value_results,
284 imm_opnums,
285 is_terminator: self.is_terminator,
286 is_branch: self.is_branch,
287 is_call: self.is_call,
288 is_return: self.is_return,
289 can_load: self.can_load,
290 can_store: self.can_store,
291 can_trap: self.can_trap,
292 other_side_effects: self.other_side_effects,
293 side_effects_idempotent: self.side_effects_idempotent,
294 inst_builder_imm_method: self.inst_builder_imm_method,
295 })
296 }
297}
298
299fn verify_format(inst_name: &str, operands_in: &[Operand], format: &InstructionFormat) {
301 let mut num_values = 0;
306 let mut num_blocks = 0;
307 let mut num_raw_blocks = 0;
308 let mut num_immediates = 0;
309
310 for operand in operands_in.iter() {
311 if operand.is_varargs() {
312 assert!(
313 format.has_value_list,
314 "instruction {} has varargs, but its format {} doesn't have a value list; you may \
315 need to use a different format.",
316 inst_name, format.name
317 );
318 }
319 if operand.is_value() {
320 num_values += 1;
321 }
322 if operand.kind.is_block() {
323 num_blocks += 1;
324 } else if operand.kind.is_raw_block() {
325 num_raw_blocks += 1;
326 } else if operand.is_immediate_or_entityref() {
327 if let Some(format_field) = format.imm_fields.get(num_immediates) {
328 assert_eq!(
329 format_field.kind.rust_field_name,
330 operand.kind.rust_field_name,
331 "{}th operand of {} should be {} (according to format), not {} (according to \
332 inst definition). You may need to use a different format.",
333 num_immediates,
334 inst_name,
335 format_field.kind.rust_field_name,
336 operand.kind.rust_field_name
337 );
338 num_immediates += 1;
339 }
340 }
341 }
342
343 assert_eq!(
344 num_values, format.num_value_operands,
345 "inst {} doesn't have as many value input operands as its format {} declares; you may need \
346 to use a different format.",
347 inst_name, format.name
348 );
349
350 assert_eq!(
351 num_blocks, format.num_block_operands,
352 "inst {} doesn't have as many block input operands as its format {} declares; you may need \
353 to use a different format.",
354 inst_name, format.name,
355 );
356
357 assert_eq!(
358 num_raw_blocks, format.num_raw_block_operands,
359 "inst {} doesn't have as many raw-block input operands as its format {} declares; you may need \
360 to use a different format.",
361 inst_name, format.name,
362 );
363
364 assert_eq!(
365 num_immediates,
366 format.imm_fields.len(),
367 "inst {} doesn't have as many immediate input \
368 operands as its format {} declares; you may need to use a different format.",
369 inst_name,
370 format.name
371 );
372}
373
374fn verify_polymorphic(
376 operands_in: &[Operand],
377 operands_out: &[Operand],
378 format: &InstructionFormat,
379 value_opnums: &[usize],
380) -> Option<PolymorphicInfo> {
381 let is_polymorphic = operands_in
383 .iter()
384 .any(|op| op.is_value() && op.type_var().unwrap().free_typevar().is_some())
385 || operands_out
386 .iter()
387 .any(|op| op.is_value() && op.type_var().unwrap().free_typevar().is_some());
388
389 if !is_polymorphic {
390 return None;
391 }
392
393 let tv_op = format.typevar_operand;
395 let mut maybe_error_message = None;
396 if let Some(tv_op) = tv_op {
397 if tv_op < value_opnums.len() {
398 let op_num = value_opnums[tv_op];
399 let tv = operands_in[op_num].type_var().unwrap();
400 let free_typevar = tv.free_typevar();
401 if (free_typevar.is_some() && tv == &free_typevar.unwrap())
402 || tv.singleton_type().is_some()
403 {
404 match is_ctrl_typevar_candidate(tv, operands_in, operands_out) {
405 Ok(_other_typevars) => {
406 return Some(PolymorphicInfo {
407 use_typevar_operand: true,
408 ctrl_typevar: tv.clone(),
409 });
410 }
411 Err(error_message) => {
412 maybe_error_message = Some(error_message);
413 }
414 }
415 }
416 }
417 };
418
419 if operands_out.is_empty() {
423 match maybe_error_message {
425 Some(msg) => panic!("{}", msg),
426 None => panic!("typevar_operand must be a free type variable"),
427 }
428 }
429
430 let tv = operands_out[0].type_var().unwrap();
432 let free_typevar = tv.free_typevar();
433 if free_typevar.is_some() && tv != &free_typevar.unwrap() {
434 panic!("first result must be a free type variable");
435 }
436
437 is_ctrl_typevar_candidate(tv, operands_in, operands_out).unwrap();
440
441 Some(PolymorphicInfo {
442 use_typevar_operand: false,
443 ctrl_typevar: tv.clone(),
444 })
445}
446
447fn is_ctrl_typevar_candidate(
457 ctrl_typevar: &TypeVar,
458 operands_in: &[Operand],
459 operands_out: &[Operand],
460) -> Result<Vec<TypeVar>, String> {
461 let mut other_typevars = Vec::new();
462
463 for input in operands_in {
465 if !input.is_value() {
466 continue;
467 }
468
469 let typ = input.type_var().unwrap();
470 let free_typevar = typ.free_typevar();
471
472 if free_typevar.is_none() {
474 continue;
475 }
476 let free_typevar = free_typevar.unwrap();
477 if &free_typevar == ctrl_typevar {
478 continue;
479 }
480
481 if typ != &free_typevar {
483 return Err(format!(
484 "{:?}: type variable {} must be derived from {:?} while it is derived from {:?}",
485 input, typ.name, ctrl_typevar, free_typevar
486 ));
487 }
488
489 for other_tv in &other_typevars {
491 if &free_typevar == other_tv {
492 return Err(format!(
493 "non-controlling type variable {} can't be used more than once",
494 free_typevar.name
495 ));
496 }
497 }
498
499 other_typevars.push(free_typevar);
500 }
501
502 for result in operands_out {
504 if !result.is_value() {
505 continue;
506 }
507
508 let typ = result.type_var().unwrap();
509 let free_typevar = typ.free_typevar();
510
511 if free_typevar.is_none() || &free_typevar.unwrap() == ctrl_typevar {
513 continue;
514 }
515
516 return Err("type variable in output not derived from ctrl_typevar".into());
517 }
518
519 Ok(other_typevars)
520}