All Downloads are FREE. Search and download functionalities are using the official Maven repository.

apparat.pbj.optimization.PbjOptimizer.scala Maven / Gradle / Ivy

/*
 * This file is part of Apparat.
 *
 * Apparat is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Apparat is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Apparat. If not, see .
 *
 * Copyright (C) 2010 Joa Ebert
 * http://www.joa-ebert.com/
 *
 */
package apparat.pbj.optimization

import apparat.pbj.Pbj
import annotation.tailrec
import apparat.pbj.pbjdata._

/**
 * The PbjOptimizer class is a 1-pass optimizer for Pbj code.
 *
 * It performs copy-propagation and dead-code-elimination in a single step.
 *
 * @author Joa Ebert
 */
object PbjOptimizer extends (Pbj => Unit) {
	private val MAX_ITERATIONS = 32

	override def apply(pbj: Pbj): Unit = {
		@tailrec def loop(i: Int = 0): Unit = i match {
			case x if x >= MAX_ITERATIONS =>
			case y => if(optimize(pbj)) loop(y+1)
		}

		loop()
	}

	/**
	 * Optimizes a given Pbj file.
	 *
	 * @return true if any modification happened; false otherwise.
	 */
	def optimize(pbj: Pbj): Boolean = {
		@tailrec def splitIntoBlocks(result: List[List[POp]],
																 in: List[POp]): List[List[POp]] = {
			//
			// Split the code into basic blocks.
			//
			// b0
			// If
			// b1
			// Else
			// b2
			// Endif
			// b3
			//
			// b0 will include the If, b1 will include the Else and b2 will include
			// the Endif statement. b3 will be the last block.
			//

			val (block, rest) = in splitAt {
				(in indexWhere {
					op => op.opCode == POp.If || op.opCode == POp.Else || op.opCode == POp.Endif
				}) + 1
			}

			if(block != Nil) {
				splitIntoBlocks(block :: result, rest)
			} else {
				(rest :: result).reverse
			}
		}

		val code = pbj.code
		val (newCode, transformed) = if((code exists { _.opCode == POp.If }) &&
			code.last.opCode != POp.If) {
			val optimizedBlocks = splitIntoBlocks(List.empty, code) map optimizeBlock(pbj, false)
			(optimizedBlocks flatMap { _._1 }) -> (optimizedBlocks exists { _._2 })
		} else {
			optimizeBlock(pbj, true)(code)
		}

		/*
		val dead = ((pbj.parameters map { _._1.register.code }) ::: (pbj.code collect {
			case l: PLogical => 0x8000
			case d: PDst => d.dst.code
		})).distinct diff ((pbj.parameters map { _._1.register.code }) ::: (pbj.code flatMap {
			case PCopy(_, src) => src.code :: Nil
			case d: PDstAndSrc => d.dst.code :: d.src.code :: Nil
			case s: PSrc => s.src.code :: Nil
			case _ => Nil
		})).distinct

		if(log.debugEnabled) {
			log.debug("PBJ globally dead:")
			for(d <- dead) log.debug("  %s", d)
		}


		pbj.code = pbj.code filterNot {
			case l: PLogical => dead contains 0x8000
			case d: PDst => dead exists { _ == d.dst.code }
			case _ => false
		}
		 */
		pbj.code = newCode
		transformed
	}

	def optimizeBlock(pbj: Pbj, killCode: Boolean)(block: List[POp]): (List[POp], Boolean )= {
		var deadCandidates = List.empty[POp]
		var copyCandidates = List.empty[PDst]
		var dead = List.empty[POp]
		var modified = false

		def updateDead(op: POp): POp = op match {
			case op: PSelect =>
				if(killCode) {
					deadCandidates = deadCandidates filterNot {
						x => (x definesAny op.dst) || (x definesAny op.src) ||
								 (x definesAny op.src0) || (x definesAny op.src1) }
					dead = (deadCandidates filter { _ definesOnly op.dst }) ::: dead
					deadCandidates = op :: deadCandidates
				}
				op
			case op: PBinop =>
				if(killCode) {
					deadCandidates = deadCandidates filterNot {
						x => (x definesAny op.src) || (x definesAny op.dst) }
					dead = (deadCandidates filter { _ definesOnly op.dst }) ::: dead
					deadCandidates = op :: deadCandidates
				}
				op
			case op: PUnop =>
				if(killCode) {
					deadCandidates = deadCandidates filterNot { _ definesAny op.src }
					dead = (deadCandidates filter { _ definesOnly op.dst }) ::: dead
					deadCandidates = op :: deadCandidates
				}
				op
			case op: PArity1 =>
				if(killCode) {
					deadCandidates = deadCandidates filterNot { _ definesAny op.src }
					dead = (deadCandidates filter { _ definesOnly op.dst }) ::: dead
					deadCandidates = op :: deadCandidates
				}
				op
			case op: PArity2 =>
				if(killCode) {
					deadCandidates = deadCandidates filterNot {
						x => (x definesAny op.src) || (x definesAny op.dst) }
					dead = (deadCandidates filter { _ definesOnly op.dst }) ::: dead
					deadCandidates = op :: deadCandidates
				}
				op
			case op: PLogical =>
				if(killCode) {
					deadCandidates = deadCandidates filterNot {
						x => (x definesAny op.src) || (x definesAny op.dst) }
					dead = (deadCandidates filter { _ definesOnly op.dst }) ::: dead
					deadCandidates = op :: deadCandidates
				}
				op
			case op: PDstAndSrc =>
				if(killCode) {
					deadCandidates = deadCandidates filterNot {
						x => (x definesAny op.src) || (x definesAny op.dst) }
					dead = (deadCandidates filter { _ definesOnly op.dst }) ::: dead
					deadCandidates = op :: deadCandidates
				}
				op
			case op: PDst =>
				if(killCode) {
					dead = (deadCandidates filter { _ definesOnly op.dst }) ::: dead
					deadCandidates = op :: deadCandidates
				}
				op
			case op: PSrc =>
				if(killCode) {
					deadCandidates = op :: deadCandidates filterNot { _ definesAny op.src }
				}
				op
			case op => op
		}

		def updateCopy(op: POp, tail: List[POp]): POp = op match {
			case op: PLogical =>
				copyCandidates = if(1 == (tail count { _ uses 0x8000 })) {//<- optimize
					op :: (copyCandidates filterNot { _ uses 0x8000 })
				} else {
					(copyCandidates filterNot { _ uses 0x8000 })
				}
				op
			case op: PDst =>
				val dst = op.dst
				copyCandidates = if(1 == (tail count { _ uses dst })) {//<- optimize
					op :: (copyCandidates filterNot { _ uses dst })
				} else {
					(copyCandidates filterNot { _ uses dst })
				}
				op
			case op => op
		}

		def transform(op: POp, tail: List[POp]): POp = op match {
			case PCopy(dst, src) =>
				updateCopy(updateDead(copyCandidates find { _ defines src } match {
					case Some(x) if x.dst.swizzle == dst.swizzle =>
						modified = true
						x mapDef dst.code
					case _ => op
				}), tail)
			//case PIf(src) => ...
			case op => updateCopy(updateDead(op), tail)
		}

		@tailrec def loop(value: List[POp],
											result: List[POp] = Nil): List[POp] = value match {
			case Nil => result.reverse
			case x :: xs => {
				val a = transform(x, xs)
				loop(xs, a :: result)
			}
		}

		val list = loop(block)

		if(killCode && deadCandidates.length > 0) {
			dead = deadCandidates ::: dead
		}

		dead = dead filterNot {
			d => pbj.parameters collect { case (POutParameter(_, _, r), _) => r } exists { d definesAny _ }
		}

		if(modified) {
			if(killCode) {
				(list filterNot { dead contains _ }, true)
			} else {
				(list, true)
			}
		} else {
			if(killCode && dead.length > 0) {
				(list filterNot { dead contains _ }, true)
			} else {
				(list, false)
			}
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy