Code/Wasm/Optimize/Peephole/PeepholeOptimization.cs
using System;
using System.Collections.Generic;
using System.Linq;
using WasmBox.Wasm.Instructions;

namespace WasmBox.Wasm.Optimize {
    /// <summary>
    /// An optimization that pattern-matches and rewrites small sequences of
    /// instructions.
    /// </summary>
    public abstract class PeepholeOptimization {
        /// <summary>
        /// Tests if the items at the front of the given list of instructions
        /// match the peephole optimization; if a match occurs, a nonzero value
        /// is returned that indicates the number of instructions at the front
        /// of the list of instructions that should be rewritten.
        /// </summary>
        /// <param name="instructions">
        /// The instructions to match against the peephole optimization.
        /// </param>
        /// <returns>The number of instructions to rewrite.</returns>
        public abstract uint Match(IReadOnlyList<Instruction> instructions);

        /// <summary>
        /// Rewrites the given sequence of instructions.
        /// </summary>
        /// <param name="matched">
        /// A list of instructions that has been matched and will all be replaced.
        /// </param>
        /// <returns>The rewritten instructions.</returns>
        public abstract IReadOnlyList<Instruction> Rewrite(IReadOnlyList<Instruction> matched);
    }

    /// <summary>
    /// An optimizer that applies peephole optimizations.
    /// </summary>
    public sealed class PeepholeOptimizer {
        /// <summary>
        /// Creates a peephole optimizer that applies the given optimizations.
        /// </summary>
        /// <param name="optimizations">The optimizations to apply.</param>
        public PeepholeOptimizer(IEnumerable<PeepholeOptimization> optimizations) {
            this.opts = optimizations;
        }

        private IEnumerable<PeepholeOptimization> opts;

        /// <summary>
        /// A peephole optimizer based that uses the default set of peephole
        /// optimizations offered by cs-wasm.
        /// </summary>
        public static PeepholeOptimizer DefaultOptimizer => new PeepholeOptimizer(DefaultOptimizations);

        /// <summary>
        /// The default set of peephole optimizations that ships with cs-wasm.
        /// </summary>
        public static readonly IEnumerable<PeepholeOptimization> DefaultOptimizations =
            new PeepholeOptimization[] {
            TeeLocalOptimization.Instance,
            UnreachableCodeOptimization.Instance
        };

        /// <summary>
        /// Uses this peephole optimizer to optimize the given sequence of instructions.
        /// </summary>
        /// <param name="instructions">The instructions to optimize.</param>
        /// <returns>An optimized sequence of instructions.</returns>
        public IReadOnlyList<Instruction> Optimize(IReadOnlyList<Instruction> instructions) {
            var inputArray = Enumerable.ToArray( instructions);
            var results = new List<Instruction>();
            for (int i = 0; i < inputArray.Length;) {
                PeepholeOptimization bestOpt;
                uint matchSize = LongestMatch(
                    new ArraySegment<Instruction>(inputArray, i, inputArray.Length - i),
                    out bestOpt);
                if (matchSize > 0) {
                    results.AddRange(
                        bestOpt.Rewrite(
                            new ArraySegment<Instruction>(inputArray, i, (int)matchSize)));
                    i += (int)matchSize;
                }
                else {
                    if (inputArray[i] is BlockInstruction) {
                        // Visit block instructions recursively.
                        var block = (BlockInstruction)inputArray[i];
                        results.Add(
                            new BlockInstruction((BlockOperator)block.Op, block.Type, Optimize(block.Contents)));
                    }
                    else if (inputArray[i] is IfElseInstruction) {
                        // Visit if-else instructions recursively, too.
                        var ifElse = (IfElseInstruction)inputArray[i];
                        results.Add(
                            new IfElseInstruction(
                                ifElse.Type,
                                ifElse.IfBranch == null ? null : Optimize(ifElse.IfBranch),
                                ifElse.ElseBranch == null ? null : Optimize(ifElse.ElseBranch)));
                    }
                    else {
                        // Other instructions are added to the list unmodified.
                        results.Add(inputArray[i]);
                    }
                    i++;
                }
            }
            return results;
        }

        private uint LongestMatch(
            IReadOnlyList<Instruction> instructions,
            out PeepholeOptimization matchingOptimization) {
            uint bestMatch = 0;
            PeepholeOptimization bestOpt = null;
            foreach (var opt in opts) {
                uint match = opt.Match(instructions);
                if (match > bestMatch) {
                    bestMatch = match;
                    bestOpt = opt;
                }
            }
            matchingOptimization = bestOpt;
            return bestMatch;
        }
    }
}