1use std::collections::btree_map;
48use std::collections::{BTreeMap, HashMap, HashSet};
49use std::convert::TryFrom;
50use std::iter::FromIterator;
51
52use cranelift_codegen_shared::constant_hash::generate_table;
53use cranelift_entity::EntityRef;
54
55use crate::error;
56use crate::srcgen::Formatter;
57
58use crate::cdsl::cpu_modes::CpuMode;
59use crate::cdsl::encodings::Encoding;
60use crate::cdsl::instructions::{Instruction, InstructionPredicate, InstructionPredicateNumber};
61use crate::cdsl::isa::TargetIsa;
62use crate::cdsl::recipes::{EncodingRecipe, OperandConstraint, Recipes, Register};
63use crate::cdsl::regs::IsaRegs;
64use crate::cdsl::settings::SettingPredicateNumber;
65use crate::cdsl::types::ValueType;
66use crate::cdsl::xform::TransformGroupIndex;
67
68use crate::shared::Definitions as SharedDefinitions;
69
70use crate::default_map::MapWithDefault;
71use crate::unique_table::UniqueSeqTable;
72
73fn emit_instp(instp: &InstructionPredicate, has_func: bool, fmt: &mut Formatter) {
79 if let Some(type_predicate) = instp.type_predicate("func") {
80 fmt.line("let args = inst.arguments(&func.dfg.value_lists);");
81 fmt.line(type_predicate);
82 return;
83 }
84
85 let leaves = instp.collect_leaves();
86
87 let mut has_type_check = false;
88 let mut format_name = None;
89 let mut field_names = HashSet::new();
90
91 for leaf in leaves {
92 if leaf.is_type_predicate() {
93 has_type_check = true;
94 } else {
95 field_names.insert(leaf.format_destructuring_member_name());
96 let leaf_format_name = leaf.format_name();
97 match format_name {
98 None => format_name = Some(leaf_format_name),
99 Some(previous_format_name) => {
100 assert!(
101 previous_format_name == leaf_format_name,
102 format!("Format predicate can only operate on a single InstructionFormat; trying to use both {} and {}", previous_format_name, leaf_format_name
103 ));
104 }
105 }
106 }
107 }
108
109 let mut fields = Vec::from_iter(field_names);
110 fields.sort();
111 let fields = fields.join(", ");
112
113 let format_name = format_name.expect("There should be a format name!");
114
115 fmtln!(
116 fmt,
117 "if let crate::ir::InstructionData::{} {{ {}, .. }} = *inst {{",
118 format_name,
119 fields
120 );
121 fmt.indent(|fmt| {
122 if has_type_check {
123 assert!(has_func, "recipe predicates can't check type variables.");
125 fmt.line("let args = inst.arguments(&func.dfg.value_lists);");
126 } else if has_func {
127 fmt.line("let _ = func;");
129 }
130 fmtln!(fmt, "return {};", instp.rust_predicate("func").unwrap());
131 });
132 fmtln!(fmt, "}");
133
134 fmt.line("unreachable!();");
135}
136
137fn emit_recipe_predicates(isa: &TargetIsa, fmt: &mut Formatter) {
143 let mut predicate_names = HashMap::new();
144
145 fmt.comment(format!("{} recipe predicates.", isa.name));
146 for recipe in isa.recipes.values() {
147 let (isap, instp) = match (&recipe.isa_predicate, &recipe.inst_predicate) {
148 (None, None) => continue,
149 (isap, instp) if predicate_names.contains_key(&(isap, instp)) => continue,
150 (isap, instp) => (isap, instp),
151 };
152
153 let func_name = format!("recipe_predicate_{}", recipe.name.to_lowercase());
154 predicate_names.insert((isap, instp), func_name.clone());
155
156 fmtln!(
158 fmt,
159 "fn {}({}: crate::settings::PredicateView, {}: &ir::InstructionData) -> bool {{",
160 func_name,
161 if isap.is_some() { "isap" } else { "_" },
162 if instp.is_some() { "inst" } else { "_" }
163 );
164 fmt.indent(|fmt| {
165 match (isap, instp) {
166 (Some(isap), None) => {
167 fmtln!(fmt, "isap.test({})", isap);
168 }
169 (None, Some(instp)) => {
170 emit_instp(instp, false, fmt);
171 }
172 (Some(isap), Some(instp)) => {
173 fmtln!(fmt, "isap.test({}) &&", isap);
174 emit_instp(instp, false, fmt);
175 }
176 _ => panic!("skipped above"),
177 }
178 });
179 fmtln!(fmt, "}");
180 }
181 fmt.empty_line();
182
183 fmt.doc_comment(format!(
185 r#"{} recipe predicate table.
186
187 One entry per recipe, set to Some only when the recipe is guarded by a predicate."#,
188 isa.name
189 ));
190 fmtln!(
191 fmt,
192 "pub static RECIPE_PREDICATES: [RecipePredicate; {}] = [",
193 isa.recipes.len()
194 );
195 fmt.indent(|fmt| {
196 for recipe in isa.recipes.values() {
197 match (&recipe.isa_predicate, &recipe.inst_predicate) {
198 (None, None) => fmt.line("None,"),
199 key => fmtln!(fmt, "Some({}),", predicate_names.get(&key).unwrap()),
200 }
201 }
202 });
203 fmtln!(fmt, "];");
204 fmt.empty_line();
205}
206
207fn emit_inst_predicates(isa: &TargetIsa, fmt: &mut Formatter) {
210 fmt.comment(format!("{} instruction predicates.", isa.name));
211 for (id, instp) in isa.encodings_predicates.iter() {
212 fmtln!(fmt, "fn inst_predicate_{}(func: &crate::ir::Function, inst: &crate::ir::InstructionData) -> bool {{", id.index());
213 fmt.indent(|fmt| {
214 emit_instp(instp, true, fmt);
215 });
216 fmtln!(fmt, "}");
217 }
218 fmt.empty_line();
219
220 fmt.doc_comment(format!(
222 r#"{} instruction predicate table.
223
224 One entry per instruction predicate, so the encoding bytecode can embed indexes into this
225 table."#,
226 isa.name
227 ));
228 fmtln!(
229 fmt,
230 "pub static INST_PREDICATES: [InstPredicate; {}] = [",
231 isa.encodings_predicates.len()
232 );
233 fmt.indent(|fmt| {
234 for id in isa.encodings_predicates.keys() {
235 fmtln!(fmt, "inst_predicate_{},", id.index());
236 }
237 });
238 fmtln!(fmt, "];");
239 fmt.empty_line();
240}
241
242fn emit_recipe_names(isa: &TargetIsa, fmt: &mut Formatter) {
246 fmt.doc_comment(format!(
247 r#"{} recipe names, using the same recipe index spaces as the one specified by the
248 corresponding binemit file."#,
249 isa.name
250 ));
251 fmtln!(
252 fmt,
253 "static RECIPE_NAMES: [&str; {}] = [",
254 isa.recipes.len()
255 );
256 fmt.indent(|fmt| {
257 for recipe in isa.recipes.values() {
258 fmtln!(fmt, r#""{}","#, recipe.name);
259 }
260 });
261 fmtln!(fmt, "];");
262 fmt.empty_line();
263}
264
265fn get_fixed_registers(operands_in: &[OperandConstraint]) -> HashSet<Register> {
267 HashSet::from_iter(
268 operands_in
269 .iter()
270 .map(|constraint| {
271 if let OperandConstraint::FixedReg(reg) = &constraint {
272 Some(*reg)
273 } else {
274 None
275 }
276 })
277 .filter(|opt| opt.is_some())
278 .map(|opt| opt.unwrap()),
279 )
280}
281
282fn emit_operand_constraints(
287 registers: &IsaRegs,
288 recipe: &EncodingRecipe,
289 constraints: &[OperandConstraint],
290 field_name: &'static str,
291 tied_operands: &HashMap<usize, usize>,
292 fixed_registers: &HashSet<Register>,
293 fmt: &mut Formatter,
294) {
295 if constraints.is_empty() {
296 fmtln!(fmt, "{}: &[],", field_name);
297 return;
298 }
299
300 fmtln!(fmt, "{}: &[", field_name);
301 fmt.indent(|fmt| {
302 for (n, constraint) in constraints.iter().enumerate() {
303 fmt.line("OperandConstraint {");
304 fmt.indent(|fmt| {
305 match constraint {
306 OperandConstraint::RegClass(reg_class) => {
307 if let Some(tied_input) = tied_operands.get(&n) {
308 fmtln!(fmt, "kind: ConstraintKind::Tied({}),", tied_input);
309 } else {
310 fmt.line("kind: ConstraintKind::Reg,");
311 }
312 fmtln!(
313 fmt,
314 "regclass: &{}_DATA,",
315 registers.classes[*reg_class].name
316 );
317 }
318 OperandConstraint::FixedReg(reg) => {
319 assert!(!tied_operands.contains_key(&n), "can't tie fixed registers");
320 let constraint_kind = if fixed_registers.contains(®) {
321 "FixedTied"
322 } else {
323 "FixedReg"
324 };
325 fmtln!(
326 fmt,
327 "kind: ConstraintKind::{}({}),",
328 constraint_kind,
329 reg.unit
330 );
331 fmtln!(
332 fmt,
333 "regclass: &{}_DATA,",
334 registers.classes[reg.regclass].name
335 );
336 }
337 OperandConstraint::TiedInput(tied_input) => {
338 assert!(
341 tied_input == tied_operands.get(&n).unwrap(),
342 "invalid tied constraint"
343 );
344 fmtln!(fmt, "kind: ConstraintKind::Tied({}),", tied_input);
345
346 let tied_class = if let OperandConstraint::RegClass(tied_class) =
347 recipe.operands_in[*tied_input]
348 {
349 tied_class
350 } else {
351 panic!("tied constraints relate only to register inputs");
352 };
353
354 fmtln!(
355 fmt,
356 "regclass: &{}_DATA,",
357 registers.classes[tied_class].name
358 );
359 }
360 OperandConstraint::Stack(stack) => {
361 assert!(!tied_operands.contains_key(&n), "can't tie stack operand");
362 fmt.line("kind: ConstraintKind::Stack,");
363 fmtln!(
364 fmt,
365 "regclass: &{}_DATA,",
366 registers.classes[stack.regclass].name
367 );
368 }
369 }
370 });
371 fmt.line("},");
372 }
373 });
374 fmtln!(fmt, "],");
375}
376
377fn emit_recipe_constraints(isa: &TargetIsa, fmt: &mut Formatter) {
381 fmt.doc_comment(format!(
382 r#"{} recipe constraints list, using the same recipe index spaces as the one
383 specified by the corresponding binemit file. These constraints are used by register
384 allocation to select the right location to use for input and output values."#,
385 isa.name
386 ));
387 fmtln!(
388 fmt,
389 "static RECIPE_CONSTRAINTS: [RecipeConstraints; {}] = [",
390 isa.recipes.len()
391 );
392 fmt.indent(|fmt| {
393 for recipe in isa.recipes.values() {
394 let mut tied_in_to_out = HashMap::new();
397 let mut tied_out_to_in = HashMap::new();
398 for (out_index, constraint) in recipe.operands_out.iter().enumerate() {
399 if let OperandConstraint::TiedInput(in_index) = &constraint {
400 tied_in_to_out.insert(*in_index, out_index);
401 tied_out_to_in.insert(out_index, *in_index);
402 }
403 }
404
405 let fixed_inputs = get_fixed_registers(&recipe.operands_in);
407 let fixed_outputs = get_fixed_registers(&recipe.operands_out);
408
409 fmt.comment(format!("Constraints for recipe {}:", recipe.name));
410 fmt.line("RecipeConstraints {");
411 fmt.indent(|fmt| {
412 emit_operand_constraints(
413 &isa.regs,
414 recipe,
415 &recipe.operands_in,
416 "ins",
417 &tied_in_to_out,
418 &fixed_outputs,
419 fmt,
420 );
421 emit_operand_constraints(
422 &isa.regs,
423 recipe,
424 &recipe.operands_out,
425 "outs",
426 &tied_out_to_in,
427 &fixed_inputs,
428 fmt,
429 );
430 fmtln!(
431 fmt,
432 "fixed_ins: {},",
433 if !fixed_inputs.is_empty() {
434 "true"
435 } else {
436 "false"
437 }
438 );
439 fmtln!(
440 fmt,
441 "fixed_outs: {},",
442 if !fixed_outputs.is_empty() {
443 "true"
444 } else {
445 "false"
446 }
447 );
448 fmtln!(
449 fmt,
450 "tied_ops: {},",
451 if !tied_in_to_out.is_empty() {
452 "true"
453 } else {
454 "false"
455 }
456 );
457 fmtln!(
458 fmt,
459 "clobbers_flags: {},",
460 if recipe.clobbers_flags {
461 "true"
462 } else {
463 "false"
464 }
465 );
466 });
467 fmt.line("},");
468 }
469 });
470 fmtln!(fmt, "];");
471 fmt.empty_line();
472}
473
474fn emit_recipe_sizing(isa: &TargetIsa, fmt: &mut Formatter) {
476 fmt.doc_comment(format!(
477 r#"{} recipe sizing descriptors, using the same recipe index spaces as the one
478 specified by the corresponding binemit file. These are used to compute the final size of an
479 instruction, as well as to compute the range of branches."#,
480 isa.name
481 ));
482 fmtln!(
483 fmt,
484 "static RECIPE_SIZING: [RecipeSizing; {}] = [",
485 isa.recipes.len()
486 );
487 fmt.indent(|fmt| {
488 for recipe in isa.recipes.values() {
489 fmt.comment(format!("Code size information for recipe {}:", recipe.name));
490 fmt.line("RecipeSizing {");
491 fmt.indent(|fmt| {
492 fmtln!(fmt, "base_size: {},", recipe.base_size);
493 fmtln!(fmt, "compute_size: {},", recipe.compute_size);
494 if let Some(range) = &recipe.branch_range {
495 fmtln!(
496 fmt,
497 "branch_range: Some(BranchRange {{ origin: {}, bits: {} }}),",
498 range.inst_size,
499 range.range
500 );
501 } else {
502 fmt.line("branch_range: None,");
503 }
504 });
505 fmt.line("},");
506 }
507 });
508 fmtln!(fmt, "];");
509 fmt.empty_line();
510}
511
512struct Level1Table<'cpu_mode> {
514 cpu_mode: &'cpu_mode CpuMode,
515 legalize_code: TransformGroupIndex,
516
517 table_map: HashMap<Option<ValueType>, usize>,
518 table_vec: Vec<Level2Table>,
519}
520
521impl<'cpu_mode> Level1Table<'cpu_mode> {
522 fn new(cpu_mode: &'cpu_mode CpuMode) -> Self {
523 Self {
524 cpu_mode,
525 legalize_code: cpu_mode.get_default_legalize_code(),
526 table_map: HashMap::new(),
527 table_vec: Vec::new(),
528 }
529 }
530
531 fn l2table_for(&mut self, typ: Option<ValueType>) -> &mut Level2Table {
533 let cpu_mode = &self.cpu_mode;
534 let index = match self.table_map.get(&typ) {
535 Some(&index) => index,
536 None => {
537 let legalize_code = cpu_mode.get_legalize_code_for(&typ);
538 let table = Level2Table::new(typ.clone(), legalize_code);
539 let index = self.table_vec.len();
540 self.table_map.insert(typ, index);
541 self.table_vec.push(table);
542 index
543 }
544 };
545 self.table_vec.get_mut(index).unwrap()
546 }
547
548 fn l2tables(&mut self) -> Vec<&mut Level2Table> {
549 self.table_vec
550 .iter_mut()
551 .filter(|table| !table.is_empty())
552 .collect::<Vec<_>>()
553 }
554}
555
556struct Level2HashTableEntry {
557 inst_name: String,
558 offset: usize,
559}
560
561struct Level2Table {
565 typ: Option<ValueType>,
566 legalize_code: TransformGroupIndex,
567 inst_to_encodings: BTreeMap<String, EncodingList>,
568 hash_table_offset: Option<usize>,
569 hash_table_len: Option<usize>,
570}
571
572impl Level2Table {
573 fn new(typ: Option<ValueType>, legalize_code: TransformGroupIndex) -> Self {
574 Self {
575 typ,
576 legalize_code,
577 inst_to_encodings: BTreeMap::new(),
578 hash_table_offset: None,
579 hash_table_len: None,
580 }
581 }
582
583 fn enclist_for(&mut self, inst: &Instruction) -> &mut EncodingList {
584 let copied_typ = self.typ.clone();
585 self.inst_to_encodings
586 .entry(inst.name.clone())
587 .or_insert_with(|| EncodingList::new(inst, copied_typ))
588 }
589
590 fn enclists(&mut self) -> btree_map::ValuesMut<'_, String, EncodingList> {
591 self.inst_to_encodings.values_mut()
592 }
593
594 fn is_empty(&self) -> bool {
595 self.inst_to_encodings.is_empty()
596 }
597
598 fn layout_hashtable(
599 &mut self,
600 level2_hashtables: &mut Vec<Option<Level2HashTableEntry>>,
601 level2_doc: &mut HashMap<usize, Vec<String>>,
602 ) {
603 let hash_table = generate_table(
604 self.inst_to_encodings.values(),
605 self.inst_to_encodings.len(),
606 |enc_list| enc_list.inst.opcode_number.index() + 1,
608 );
609
610 let hash_table_offset = level2_hashtables.len();
611 let hash_table_len = hash_table.len();
612
613 assert!(self.hash_table_offset.is_none());
614 assert!(self.hash_table_len.is_none());
615 self.hash_table_offset = Some(hash_table_offset);
616 self.hash_table_len = Some(hash_table_len);
617
618 level2_hashtables.extend(hash_table.iter().map(|opt_enc_list| {
619 opt_enc_list.map(|enc_list| Level2HashTableEntry {
620 inst_name: enc_list.inst.camel_name.clone(),
621 offset: enc_list.offset.unwrap(),
622 })
623 }));
624
625 let typ_comment = match &self.typ {
626 Some(ty) => ty.to_string(),
627 None => "typeless".into(),
628 };
629
630 level2_doc.get_or_default(hash_table_offset).push(format!(
631 "{:06x}: {}, {} entries",
632 hash_table_offset, typ_comment, hash_table_len
633 ));
634 }
635}
636
637const CODE_BITS: usize = 16;
661
662const PRED_START: u16 = 0x1000;
664
665const PRED_BITS: usize = 12;
667
668const PRED_MASK: usize = (1 << PRED_BITS) - 1;
670
671struct Encoder {
673 num_instruction_predicates: usize,
674
675 words: Vec<u16>,
677
678 docs: Vec<(usize, String)>,
680}
681
682impl Encoder {
683 fn new(num_instruction_predicates: usize) -> Self {
684 Self {
685 num_instruction_predicates,
686 words: Vec::new(),
687 docs: Vec::new(),
688 }
689 }
690
691 fn recipe(&mut self, recipes: &Recipes, enc: &Encoding, is_final: bool) {
693 let code = (2 * enc.recipe.index() + if is_final { 1 } else { 0 }) as u16;
694 assert!(code < PRED_START);
695
696 let doc = format!(
697 "--> {}{}",
698 enc.to_rust_comment(recipes),
699 if is_final { " and stop" } else { "" }
700 );
701 self.docs.push((self.words.len(), doc));
702
703 self.words.push(code);
704 self.words.push(enc.encbits);
705 }
706
707 fn pred(&mut self, pred_comment: String, skip: usize, n: usize) {
709 assert!(n <= PRED_MASK);
710 let entry = (PRED_START as usize) + (n | (skip << PRED_BITS));
711 assert!(entry < (1 << CODE_BITS));
712 let entry = entry as u16;
713
714 let doc = if skip == 0 {
715 "stop".to_string()
716 } else {
717 format!("skip {}", skip)
718 };
719 let doc = format!("{} unless {}", doc, pred_comment);
720
721 self.docs.push((self.words.len(), doc));
722 self.words.push(entry);
723 }
724
725 fn inst_predicate(&mut self, pred: InstructionPredicateNumber, skip: usize) {
727 let number = pred.index();
728 let pred_comment = format!("inst_predicate_{}", number);
729 self.pred(pred_comment, skip, number);
730 }
731
732 fn isa_predicate(&mut self, pred: SettingPredicateNumber, skip: usize) {
734 let n = self.num_instruction_predicates + (pred as usize);
736 let pred_comment = format!("PredicateView({})", pred);
737 self.pred(pred_comment, skip, n);
738 }
739}
740
741struct EncodingList {
746 inst: Instruction,
747 typ: Option<ValueType>,
748 encodings: Vec<Encoding>,
749 offset: Option<usize>,
750}
751
752impl EncodingList {
753 fn new(inst: &Instruction, typ: Option<ValueType>) -> Self {
754 Self {
755 inst: inst.clone(),
756 typ,
757 encodings: Default::default(),
758 offset: None,
759 }
760 }
761
762 fn encode(
769 &mut self,
770 isa: &TargetIsa,
771 cpu_mode: &CpuMode,
772 enc_lists: &mut UniqueSeqTable<u16>,
773 enc_lists_doc: &mut HashMap<usize, Vec<String>>,
774 ) {
775 assert!(!self.encodings.is_empty());
776
777 let mut encoder = Encoder::new(isa.encodings_predicates.len());
778
779 let mut index = 0;
780 while index < self.encodings.len() {
781 let encoding = &self.encodings[index];
782
783 let (isa_predicate, inst_predicate) =
790 (&encoding.isa_predicate, &encoding.inst_predicate);
791
792 let group_size = {
793 let mut group_size = 1;
794 while index + group_size < self.encodings.len() {
795 let next_encoding = &self.encodings[index + group_size];
796 if &next_encoding.inst_predicate != inst_predicate
797 || &next_encoding.isa_predicate != isa_predicate
798 {
799 break;
800 }
801 group_size += 1;
802 }
803 group_size
804 };
805
806 let is_last_group = index + group_size == self.encodings.len();
807
808 let mut skip = if is_last_group {
812 0
813 } else {
814 let isap_size = match isa_predicate {
815 Some(_) => 1,
816 None => 0,
817 };
818 let instp_size = match inst_predicate {
819 Some(_) => 1,
820 None => 0,
821 };
822 isap_size + instp_size + group_size * 2 - 1
823 };
824
825 if let Some(pred) = isa_predicate {
826 encoder.isa_predicate(*pred, skip);
827 if !is_last_group {
828 skip -= 1;
829 }
830 }
831
832 if let Some(pred) = inst_predicate {
833 encoder.inst_predicate(*pred, skip);
834 }
836
837 for i in 0..group_size {
838 let encoding = &self.encodings[index + i];
839 let is_last_encoding = index + i == self.encodings.len() - 1;
840 encoder.recipe(&isa.recipes, encoding, is_last_encoding);
841 }
842
843 index += group_size;
844 }
845
846 assert!(self.offset.is_none());
847 let offset = enc_lists.add(&encoder.words);
848 self.offset = Some(offset);
849
850 let recipe_typ_mode_name = format!(
852 "{}{} ({})",
853 self.inst.name,
854 if let Some(typ) = &self.typ {
855 format!(".{}", typ.to_string())
856 } else {
857 "".into()
858 },
859 cpu_mode.name
860 );
861
862 enc_lists_doc
863 .get_or_default(offset)
864 .push(format!("{:06x}: {}", offset, recipe_typ_mode_name));
865 for (pos, doc) in encoder.docs {
866 enc_lists_doc.get_or_default(offset + pos).push(doc);
867 }
868 enc_lists_doc
869 .get_or_default(offset + encoder.words.len())
870 .insert(0, format!("end of {}", recipe_typ_mode_name));
871 }
872}
873
874fn make_tables(cpu_mode: &CpuMode) -> Level1Table {
875 let mut table = Level1Table::new(cpu_mode);
876
877 for encoding in &cpu_mode.encodings {
878 table
879 .l2table_for(encoding.bound_type.clone())
880 .enclist_for(encoding.inst())
881 .encodings
882 .push(encoding.clone());
883 }
884
885 for value_type in cpu_mode.get_legalized_types() {
887 table.l2table_for(Some(value_type.clone()));
888 }
889 table.l2table_for(None);
891
892 table
893}
894
895fn encode_enclists(
897 isa: &TargetIsa,
898 cpu_mode: &CpuMode,
899 level1: &mut Level1Table,
900 enc_lists: &mut UniqueSeqTable<u16>,
901 enc_lists_doc: &mut HashMap<usize, Vec<String>>,
902) {
903 for level2 in level1.l2tables() {
904 for enclist in level2.enclists() {
905 enclist.encode(isa, cpu_mode, enc_lists, enc_lists_doc);
906 }
907 }
908}
909
910fn encode_level2_hashtables<'a>(
911 level1: &'a mut Level1Table,
912 level2_hashtables: &mut Vec<Option<Level2HashTableEntry>>,
913 level2_doc: &mut HashMap<usize, Vec<String>>,
914) {
915 for level2 in level1.l2tables() {
916 level2.layout_hashtable(level2_hashtables, level2_doc);
917 }
918}
919
920fn emit_encoding_tables(defs: &SharedDefinitions, isa: &TargetIsa, fmt: &mut Formatter) {
921 let mut level1_tables: HashMap<&'static str, Level1Table> = HashMap::new();
923
924 let mut level2_hashtables = Vec::new();
926 let mut level2_doc: HashMap<usize, Vec<String>> = HashMap::new();
927
928 let mut enc_lists = UniqueSeqTable::new();
930 let mut enc_lists_doc = HashMap::new();
931
932 for cpu_mode in &isa.cpu_modes {
933 level2_doc
934 .get_or_default(level2_hashtables.len())
935 .push(cpu_mode.name.into());
936
937 let mut level1 = make_tables(cpu_mode);
938
939 encode_enclists(
940 isa,
941 cpu_mode,
942 &mut level1,
943 &mut enc_lists,
944 &mut enc_lists_doc,
945 );
946 encode_level2_hashtables(&mut level1, &mut level2_hashtables, &mut level2_doc);
947
948 level1_tables.insert(cpu_mode.name, level1);
949 }
950
951 let offset_type = |length: usize| {
953 if length <= 0x10000 {
954 "u16"
955 } else {
956 assert!(u32::try_from(length).is_ok(), "table too big!");
957 "u32"
958 }
959 };
960
961 let level1_offset_type = offset_type(level2_hashtables.len());
962 let level2_offset_type = offset_type(enc_lists.len());
963
964 fmt.doc_comment(
966 format!(r#"{} encoding lists.
967
968 This contains the entire encodings bytecode for every single instruction; the encodings
969 interpreter knows where to start from thanks to the initial lookup in the level 1 and level 2
970 table entries below."#, isa.name)
971 );
972 fmtln!(fmt, "pub static ENCLISTS: [u16; {}] = [", enc_lists.len());
973 fmt.indent(|fmt| {
974 let mut line = Vec::new();
975 for (index, entry) in enc_lists.iter().enumerate() {
976 if let Some(comments) = enc_lists_doc.get(&index) {
977 if !line.is_empty() {
978 fmtln!(fmt, "{},", line.join(", "));
979 line.clear();
980 }
981 for comment in comments {
982 fmt.comment(comment);
983 }
984 }
985 line.push(format!("{:#06x}", entry));
986 }
987 if !line.is_empty() {
988 fmtln!(fmt, "{},", line.join(", "));
989 }
990 });
991 fmtln!(fmt, "];");
992 fmt.empty_line();
993
994 fmt.doc_comment(format!(
996 r#"{} level 2 hash tables.
997
998 This hash table, keyed by instruction opcode, contains all the starting offsets for the
999 encodings interpreter, for all the CPU modes. It is jumped to after a lookup on the
1000 instruction's controlling type in the level 1 hash table."#,
1001 isa.name
1002 ));
1003 fmtln!(
1004 fmt,
1005 "pub static LEVEL2: [Level2Entry<{}>; {}] = [",
1006 level2_offset_type,
1007 level2_hashtables.len()
1008 );
1009 fmt.indent(|fmt| {
1010 for (offset, entry) in level2_hashtables.iter().enumerate() {
1011 if let Some(comments) = level2_doc.get(&offset) {
1012 for comment in comments {
1013 fmt.comment(comment);
1014 }
1015 }
1016 if let Some(entry) = entry {
1017 fmtln!(
1018 fmt,
1019 "Level2Entry {{ opcode: Some(crate::ir::Opcode::{}), offset: {:#08x} }},",
1020 entry.inst_name,
1021 entry.offset
1022 );
1023 } else {
1024 fmt.line("Level2Entry { opcode: None, offset: 0 },");
1025 }
1026 }
1027 });
1028 fmtln!(fmt, "];");
1029 fmt.empty_line();
1030
1031 for cpu_mode in &isa.cpu_modes {
1033 let level1 = &level1_tables.get(cpu_mode.name).unwrap();
1034 let hash_table = generate_table(
1035 level1.table_vec.iter(),
1036 level1.table_vec.len(),
1037 |level2_table| {
1038 if let Some(typ) = &level2_table.typ {
1039 typ.number().expect("type without a number") as usize
1040 } else {
1041 0
1042 }
1043 },
1044 );
1045
1046 fmt.doc_comment(format!(
1047 r#"{} level 1 hash table for the CPU mode {}.
1048
1049 This hash table, keyed by instruction controlling type, contains all the level 2
1050 hash-tables offsets for the given CPU mode, as well as a legalization identifier indicating
1051 which legalization scheme to apply when the instruction doesn't have any valid encoding for
1052 this CPU mode.
1053 "#,
1054 isa.name, cpu_mode.name
1055 ));
1056 fmtln!(
1057 fmt,
1058 "pub static LEVEL1_{}: [Level1Entry<{}>; {}] = [",
1059 cpu_mode.name.to_uppercase(),
1060 level1_offset_type,
1061 hash_table.len()
1062 );
1063 fmt.indent(|fmt| {
1064 for opt_level2 in hash_table {
1065 let level2 = match opt_level2 {
1066 None => {
1067 fmtln!(fmt, "Level1Entry {{ ty: ir::types::INVALID, log2len: !0, offset: 0, legalize: {} }},",
1069 isa.translate_group_index(level1.legalize_code));
1070 continue;
1071 }
1072 Some(level2) => level2,
1073 };
1074
1075 let legalize_comment = defs.transform_groups.get(level2.legalize_code).name;
1076 let legalize_code = isa.translate_group_index(level2.legalize_code);
1077
1078 let typ_name = if let Some(typ) = &level2.typ {
1079 typ.rust_name()
1080 } else {
1081 "ir::types::INVALID".into()
1082 };
1083
1084 if level2.is_empty() {
1085 fmtln!(fmt, "Level1Entry {{ ty: {}, log2len: 0, offset: !0 - 1, legalize: {} }}, // {}",
1090 typ_name, legalize_code, legalize_comment);
1091 continue;
1092 }
1093
1094 let l2l = (level2.hash_table_len.unwrap() as f64).log2() as i32;
1096 assert!(l2l > 0, "Level2 hash table was too small.");
1097 fmtln!(fmt, "Level1Entry {{ ty: {}, log2len: {}, offset: {:#08x}, legalize: {} }}, // {}",
1098 typ_name, l2l, level2.hash_table_offset.unwrap(), legalize_code, legalize_comment);
1099 }
1100 });
1101 fmtln!(fmt, "];");
1102 fmt.empty_line();
1103 }
1104}
1105
1106fn gen_isa(defs: &SharedDefinitions, isa: &TargetIsa, fmt: &mut Formatter) {
1107 emit_recipe_predicates(isa, fmt);
1109
1110 emit_inst_predicates(isa, fmt);
1112
1113 emit_encoding_tables(defs, isa, fmt);
1114
1115 emit_recipe_names(isa, fmt);
1116 emit_recipe_constraints(isa, fmt);
1117 emit_recipe_sizing(isa, fmt);
1118
1119 fmt.line("pub static INFO: isa::EncInfo = isa::EncInfo {");
1121 fmt.indent(|fmt| {
1122 fmt.line("constraints: &RECIPE_CONSTRAINTS,");
1123 fmt.line("sizing: &RECIPE_SIZING,");
1124 fmt.line("names: &RECIPE_NAMES,");
1125 });
1126 fmt.line("};");
1127}
1128
1129pub(crate) fn generate(
1130 defs: &SharedDefinitions,
1131 isa: &TargetIsa,
1132 filename: &str,
1133 out_dir: &str,
1134) -> Result<(), error::Error> {
1135 let mut fmt = Formatter::new();
1136 gen_isa(defs, isa, &mut fmt);
1137 fmt.update_file(filename, out_dir)?;
1138 Ok(())
1139}