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_raw_blocks = 0;
292 let mut num_immediates = 0;
293
294 for operand in operands_in.iter() {
295 if operand.is_varargs() {
296 assert!(
297 format.has_value_list,
298 "instruction {} has varargs, but its format {} doesn't have a value list; you may \
299 need to use a different format.",
300 inst_name, format.name
301 );
302 }
303 if operand.is_value() {
304 num_values += 1;
305 }
306 if operand.kind.is_block() {
307 num_blocks += 1;
308 } else if operand.kind.is_raw_block() {
309 num_raw_blocks += 1;
310 } else if operand.is_immediate_or_entityref() {
311 if let Some(format_field) = format.imm_fields.get(num_immediates) {
312 assert_eq!(
313 format_field.kind.rust_field_name,
314 operand.kind.rust_field_name,
315 "{}th operand of {} should be {} (according to format), not {} (according to \
316 inst definition). You may need to use a different format.",
317 num_immediates,
318 inst_name,
319 format_field.kind.rust_field_name,
320 operand.kind.rust_field_name
321 );
322 num_immediates += 1;
323 }
324 }
325 }
326
327 assert_eq!(
328 num_values, format.num_value_operands,
329 "inst {} doesn't have as many value input operands as its format {} declares; you may need \
330 to use a different format.",
331 inst_name, format.name
332 );
333
334 assert_eq!(
335 num_blocks, format.num_block_operands,
336 "inst {} doesn't have as many block input operands as its format {} declares; you may need \
337 to use a different format.",
338 inst_name, format.name,
339 );
340
341 assert_eq!(
342 num_raw_blocks, format.num_raw_block_operands,
343 "inst {} doesn't have as many raw-block input operands as its format {} declares; you may need \
344 to use a different format.",
345 inst_name, format.name,
346 );
347
348 assert_eq!(
349 num_immediates,
350 format.imm_fields.len(),
351 "inst {} doesn't have as many immediate input \
352 operands as its format {} declares; you may need to use a different format.",
353 inst_name,
354 format.name
355 );
356}
357
358fn verify_polymorphic(
360 operands_in: &[Operand],
361 operands_out: &[Operand],
362 format: &InstructionFormat,
363 value_opnums: &[usize],
364) -> Option<PolymorphicInfo> {
365 let is_polymorphic = operands_in
367 .iter()
368 .any(|op| op.is_value() && op.type_var().unwrap().free_typevar().is_some())
369 || operands_out
370 .iter()
371 .any(|op| op.is_value() && op.type_var().unwrap().free_typevar().is_some());
372
373 if !is_polymorphic {
374 return None;
375 }
376
377 let tv_op = format.typevar_operand;
379 let mut maybe_error_message = None;
380 if let Some(tv_op) = tv_op {
381 if tv_op < value_opnums.len() {
382 let op_num = value_opnums[tv_op];
383 let tv = operands_in[op_num].type_var().unwrap();
384 let free_typevar = tv.free_typevar();
385 if (free_typevar.is_some() && tv == &free_typevar.unwrap())
386 || tv.singleton_type().is_some()
387 {
388 match is_ctrl_typevar_candidate(tv, operands_in, operands_out) {
389 Ok(_other_typevars) => {
390 return Some(PolymorphicInfo {
391 use_typevar_operand: true,
392 ctrl_typevar: tv.clone(),
393 });
394 }
395 Err(error_message) => {
396 maybe_error_message = Some(error_message);
397 }
398 }
399 }
400 }
401 };
402
403 if operands_out.is_empty() {
407 match maybe_error_message {
409 Some(msg) => panic!("{}", msg),
410 None => panic!("typevar_operand must be a free type variable"),
411 }
412 }
413
414 let tv = operands_out[0].type_var().unwrap();
416 let free_typevar = tv.free_typevar();
417 if free_typevar.is_some() && tv != &free_typevar.unwrap() {
418 panic!("first result must be a free type variable");
419 }
420
421 is_ctrl_typevar_candidate(tv, operands_in, operands_out).unwrap();
424
425 Some(PolymorphicInfo {
426 use_typevar_operand: false,
427 ctrl_typevar: tv.clone(),
428 })
429}
430
431fn is_ctrl_typevar_candidate(
441 ctrl_typevar: &TypeVar,
442 operands_in: &[Operand],
443 operands_out: &[Operand],
444) -> Result<Vec<TypeVar>, String> {
445 let mut other_typevars = Vec::new();
446
447 for input in operands_in {
449 if !input.is_value() {
450 continue;
451 }
452
453 let typ = input.type_var().unwrap();
454 let free_typevar = typ.free_typevar();
455
456 if free_typevar.is_none() {
458 continue;
459 }
460 let free_typevar = free_typevar.unwrap();
461 if &free_typevar == ctrl_typevar {
462 continue;
463 }
464
465 if typ != &free_typevar {
467 return Err(format!(
468 "{:?}: type variable {} must be derived from {:?} while it is derived from {:?}",
469 input, typ.name, ctrl_typevar, free_typevar
470 ));
471 }
472
473 for other_tv in &other_typevars {
475 if &free_typevar == other_tv {
476 return Err(format!(
477 "non-controlling type variable {} can't be used more than once",
478 free_typevar.name
479 ));
480 }
481 }
482
483 other_typevars.push(free_typevar);
484 }
485
486 for result in operands_out {
488 if !result.is_value() {
489 continue;
490 }
491
492 let typ = result.type_var().unwrap();
493 let free_typevar = typ.free_typevar();
494
495 if free_typevar.is_none() || &free_typevar.unwrap() == ctrl_typevar {
497 continue;
498 }
499
500 return Err("type variable in output not derived from ctrl_typevar".into());
501 }
502
503 Ok(other_typevars)
504}