/// <summary> /// The last Rule is often a 'fallback' rule with an empty condition. If this is the case, /// we can sometimes perform an optimisation that reduces the overall code size. /// </summary> /// <returns>Root of the generated tree.</returns> private Node?BuildFallbackSequence() { // sanity check if (ruleSet.NumRules <= 1) { return(null); } // check that there is a fallback rule RuleSetEntry lastRule = ruleSet[ruleSet.NumRules - 1]; if (!lastRule.EffectiveCondition.IsEmpty) { return(null); } // check that sequence nodes are permitted if (!spec.Config.AllowSequence) { return(null); } if (spec.Config.Verbose) { Console.WriteLine("Performing fallback sequence optimisation."); } // remove the fallback rule and build the tree RuleSet rSet = ruleSet.DeriveExcludingLast(); var builder = new TreeBuilder(spec, rSet, this); Node node = builder.Build(); // ensure that there is a sequence node including the root // of the tree SequenceNode seq; if (node is SequenceNode) { seq = (SequenceNode)node; } else { seq = new SequenceNode(spec); seq.Append(node); } // now paste the fallback rule onto the end of the sequence seq.Append(lastRule.Rule); return(seq); }
/// <summary> /// The sole publicly-visible entry point. Generates a tree, given a Specification. /// </summary> /// <param name="spec">The decoder specification.</param> /// <returns>The root of the decoder tree.</returns> public static Node BuildTree(Specification spec, TristateBitArray?flags) { if (flags == null) { flags = new TristateBitArray(spec.NumFlags); } // initialise a TreeBuilder with an empty condition and a RuleSet // including all rules. var initialCondition = new Condition(spec, new TristateBitArray(spec.NumBits), flags); RuleSet ruleSet = new RuleSet(spec).Derive(initialCondition); var builder = new TreeBuilder(spec, ruleSet); return(builder.Build()); }
/// <summary> /// Build a decoder subtree for the given switch case. /// </summary> /// <param name="node">The enclosing switch node.</param> /// <param name="value">Value of case (eg., case 0x0 has value 0).</param> /// <returns>Decoder subtree.</returns> private Node BuildSwitchCase(SwitchNode node, int value) { Debug.Assert(value >= 0 && value < node.Expression.NumValues); if (spec.Config.Verbose) { Console.WriteLine("Switch case {0}", value); } // generate a derived RuleSet for this switch case var cond = new Condition(spec, node.Expression.GetBitsForValue(value), new TristateBitArray(spec.NumFlags)); RuleSet derivedSet = ruleSet.Derive(cond); // generate a corresponding TreeBuilder and update its totals for // switch bits & nesting depth var builder = new TreeBuilder(spec, derivedSet, this); builder.totalSwitchBits += node.Expression.NumBits; builder.switchNestingDepth++; // use it to build tree for case label Node child = builder.Build(); // search previously-generated switch cases for functionally identical subtrees for (int previousValue = 0; previousValue < value; previousValue++) { if (node[previousValue].Equals(child)) { if (spec.Config.Verbose) { Console.WriteLine("Pruning child {0} => {1}", value, previousValue); } // replace generated subtree with a reference to the (identical) subtree child = new ChildReferenceNode(spec, previousValue); break; } } return(child); }
/// <summary> /// Try to improve efficiency by 'lifting' a test higher up the decoder tree. This is /// only done if the test is identical for every member of a rule set. /// </summary> private Node?BuildLift(string name, Func <Condition, TristateBitArray> extract, Func <TristateBitArray, Condition> init) { if (ruleSet.NumRules < 2) { return(null); } // extract component from 1st rule & verify that flags are specified TristateBitArray component = extract(ruleSet[0].EffectiveCondition); if (component.IsEmpty) { return(null); } // check that all other rules have the same component spec for (int i = 1; i < ruleSet.NumRules; i++) { if (!extract(ruleSet[i].EffectiveCondition).IsEqual(component)) { return(null); } } if (spec.Config.Verbose) { Console.WriteLine($"Performing {name} lifting optimisation."); } // build subtree without flags Condition cond = init(component); RuleSet rSet = ruleSet.Derive(cond); var builder = new TreeBuilder(spec, rSet, this); Node node = builder.Build(); // and attach it to an if-node return(new IfElseNode(spec, cond, node, new EmptyNode(spec))); }