1 use std::fmt;
2 use std::rc::Rc;
3
4 use crate::cdsl::camel_case;
5 use crate::cdsl::formats::InstructionFormat;
6 use crate::cdsl::operands::Operand;
7 use crate::cdsl::typevar::TypeVar;
8
9 pub(crate) type AllInstructions = Vec<Instruction>;
10
11 pub(crate) struct InstructionGroupBuilder<'all_inst> {
12 all_instructions: &'all_inst mut AllInstructions,
13 }
14
15 impl<'all_inst> InstructionGroupBuilder<'all_inst> {
new(all_instructions: &'all_inst mut AllInstructions) -> Self16 pub fn new(all_instructions: &'all_inst mut AllInstructions) -> Self {
17 Self { all_instructions }
18 }
19
push(&mut self, builder: InstructionBuilder)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)]
27 pub(crate) struct PolymorphicInfo {
28 pub use_typevar_operand: bool,
29 pub ctrl_typevar: TypeVar,
30 }
31
32 #[derive(Debug)]
33 pub(crate) struct InstructionContent {
34 /// Instruction mnemonic, also becomes opcode name.
35 pub name: String,
36 pub camel_name: String,
37
38 /// Documentation string.
39 pub doc: String,
40
41 /// Input operands. This can be a mix of SSA value operands and other operand kinds.
42 pub operands_in: Vec<Operand>,
43 /// Output operands. The output operands must be SSA values or `variable_args`.
44 pub operands_out: Vec<Operand>,
45
46 /// Instruction format.
47 pub format: Rc<InstructionFormat>,
48
49 /// One of the input or output operands is a free type variable. None if the instruction is not
50 /// polymorphic, set otherwise.
51 pub polymorphic_info: Option<PolymorphicInfo>,
52
53 /// Indices in operands_in of input operands that are values.
54 pub value_opnums: Vec<usize>,
55 /// Indices in operands_in of input operands that are immediates or entities.
56 pub imm_opnums: Vec<usize>,
57 /// Indices in operands_out of output operands that are values.
58 pub value_results: Vec<usize>,
59
60 /// True for instructions that terminate the block.
61 pub is_terminator: bool,
62 /// True for all branch or jump instructions.
63 pub is_branch: bool,
64 /// Is this a call instruction?
65 pub is_call: bool,
66 /// Is this a return instruction?
67 pub is_return: bool,
68 /// Can this instruction read from memory?
69 pub can_load: bool,
70 /// Can this instruction write to memory?
71 pub can_store: bool,
72 /// Can this instruction cause a trap?
73 pub can_trap: bool,
74 /// Does this instruction have other side effects besides can_* flags?
75 pub other_side_effects: bool,
76 /// Despite having other side effects, is this instruction okay to GVN?
77 pub side_effects_idempotent: bool,
78 }
79
80 impl InstructionContent {
snake_name(&self) -> &str81 pub fn snake_name(&self) -> &str {
82 if &self.name == "return" {
83 "return_"
84 } else {
85 &self.name
86 }
87 }
88 }
89
90 pub(crate) type Instruction = Rc<InstructionContent>;
91
92 impl fmt::Display for InstructionContent {
fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>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
122 pub(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 // See Instruction comments for the meaning of these fields.
130 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
141 impl InstructionBuilder {
new<S: Into<String>>(name: S, doc: S, format: &Rc<InstructionFormat>) -> Self142 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
operands_in(mut self, operands: Vec<Operand>) -> Self162 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
operands_out(mut self, operands: Vec<Operand>) -> Self168 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 /// Mark this instruction as a block terminator.
terminates_block(mut self) -> Self175 pub fn terminates_block(mut self) -> Self {
176 self.is_terminator = true;
177 self
178 }
179
180 /// Mark this instruction as a branch instruction. This also implies that the instruction is a
181 /// block terminator.
branches(mut self) -> Self182 pub fn branches(mut self) -> Self {
183 self.is_branch = true;
184 self.terminates_block()
185 }
186
187 /// Mark this instruction as a call instruction.
call(mut self) -> Self188 pub fn call(mut self) -> Self {
189 self.is_call = true;
190 self
191 }
192
193 /// Mark this instruction as a return instruction. This also implies that the instruction is a
194 /// block terminator.
returns(mut self) -> Self195 pub fn returns(mut self) -> Self {
196 self.is_return = true;
197 self.terminates_block()
198 }
199
200 /// Mark this instruction as one that can load from memory.
can_load(mut self) -> Self201 pub fn can_load(mut self) -> Self {
202 self.can_load = true;
203 self
204 }
205
206 /// Mark this instruction as one that can store to memory.
can_store(mut self) -> Self207 pub fn can_store(mut self) -> Self {
208 self.can_store = true;
209 self
210 }
211
212 /// Mark this instruction as possibly trapping.
can_trap(mut self) -> Self213 pub fn can_trap(mut self) -> Self {
214 self.can_trap = true;
215 self
216 }
217
218 /// Mark this instruction as one that has side-effects.
other_side_effects(mut self) -> Self219 pub fn other_side_effects(mut self) -> Self {
220 self.other_side_effects = true;
221 self
222 }
223
224 /// Mark this instruction as one whose side-effects may be de-duplicated.
side_effects_idempotent(mut self) -> Self225 pub fn side_effects_idempotent(mut self) -> Self {
226 self.side_effects_idempotent = true;
227 self
228 }
229
build(self) -> Instruction230 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
283 /// Checks that the input operands actually match the given format.
verify_format(inst_name: &str, operands_in: &[Operand], format: &InstructionFormat)284 fn verify_format(inst_name: &str, operands_in: &[Operand], format: &InstructionFormat) {
285 // A format is defined by:
286 // - its number of input value operands,
287 // - its number and names of input immediate operands,
288 // - whether it has a value list or not.
289 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
358 /// Check if this instruction is polymorphic, and verify its use of type variables.
verify_polymorphic( operands_in: &[Operand], operands_out: &[Operand], format: &InstructionFormat, value_opnums: &[usize], ) -> Option<PolymorphicInfo>359 fn verify_polymorphic(
360 operands_in: &[Operand],
361 operands_out: &[Operand],
362 format: &InstructionFormat,
363 value_opnums: &[usize],
364 ) -> Option<PolymorphicInfo> {
365 // The instruction is polymorphic if it has one free input or output operand.
366 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 // Verify the use of type variables.
378 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 we reached here, it means the type variable indicated as the typevar operand couldn't
404 // control every other input and output type variable. We need to look at the result type
405 // variables.
406 if operands_out.is_empty() {
407 // No result means no other possible type variable, so it's a type inference failure.
408 match maybe_error_message {
409 Some(msg) => panic!("{}", msg),
410 None => panic!("typevar_operand must be a free type variable"),
411 }
412 }
413
414 // Otherwise, try to infer the controlling type variable by looking at the first result.
415 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 // At this point, if the next unwrap() fails, it means the output type couldn't be used as a
422 // controlling type variable either; panicking is the right behavior.
423 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
431 /// Verify that the use of TypeVars is consistent with `ctrl_typevar` as the controlling type
432 /// variable.
433 ///
434 /// All polymorphic inputs must either be derived from `ctrl_typevar` or be independent free type
435 /// variables only used once.
436 ///
437 /// All polymorphic results must be derived from `ctrl_typevar`.
438 ///
439 /// Return a vector of other type variables used, or a string explaining what went wrong.
is_ctrl_typevar_candidate( ctrl_typevar: &TypeVar, operands_in: &[Operand], operands_out: &[Operand], ) -> Result<Vec<TypeVar>, String>440 fn 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 // Check value inputs.
448 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 // Non-polymorphic or derived from ctrl_typevar is OK.
457 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 // No other derived typevars allowed.
466 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 // Other free type variables can only be used once each.
474 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 // Check outputs.
487 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 // Non-polymorphic or derived from ctrl_typevar is OK.
496 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 }
505