package org.simantics.scl.compiler.internal.codegen.analysis; import org.simantics.scl.compiler.common.names.Name; import org.simantics.scl.compiler.common.names.Names; import org.simantics.scl.compiler.constants.SCLConstant; import org.simantics.scl.compiler.internal.codegen.references.Val; import org.simantics.scl.compiler.internal.codegen.references.ValRef; import org.simantics.scl.compiler.internal.codegen.ssa.SSABlock; import org.simantics.scl.compiler.internal.codegen.ssa.SSAFunction; import org.simantics.scl.compiler.internal.codegen.ssa.binders.ValRefBinder; import org.simantics.scl.compiler.internal.codegen.ssa.statements.LetApply; import gnu.trove.set.hash.THashSet; public class LoopAnalysis { /** * Checks if it is possible to return to the loop after exiting * before exiting the function where the block belongs to. */ private static boolean isLoopingBlock(SSABlock block) { THashSet handled = new THashSet(); for(SSABlock succBlock : block.getExit().getSuccessors()) if(isLoopingBlock(block, handled, succBlock)) return true; return false; } /** * Checks if it is possible to execute block more than once without visiting * the breaker block. It is assumed that breaker is a dominator of the block. */ public static boolean isLoopingBlockWithBreaker(SSABlock block, SSABlock breaker) { SSAFunction function = block.getParent(); if(function == block.getParent()) { THashSet handled = new THashSet(); handled.add(breaker); for(SSABlock succBlock : block.getExit().getSuccessors()) if(isLoopingBlock(block, handled, succBlock)) return true; return false; } else { if(isLoopingBlock(block)) return false; Val functionTarget = function.getTarget(); if(functionTarget.hasMoreThanOneOccurences()) return false; ValRef occ = functionTarget.getOccurrence(); if(occ == null) return false; ValRefBinder occParent = occ.getParent(); if(!(occParent instanceof LetApply)) return true; // Not necessarily the right answer, but we don't try to analyse further LetApply apply = (LetApply)occParent; if(!isAppliedAtMostOnce(apply, occ, function)) return true; return isLoopingBlockWithBreaker(apply.getParent(), breaker); } } private static boolean isAppliedAtMostOnce(LetApply apply, ValRef funRef, SSAFunction function) { ValRef applyFunctionRef = apply.getFunction(); if(funRef == applyFunctionRef) { return apply.getParameters().length == function.getArity(); } Val applyFunction = applyFunctionRef.getBinding(); if(!(applyFunction instanceof SCLConstant)) return false; // Not necessarily the right answer Name name = ((SCLConstant)applyFunction).getName(); if(name == Names.Prelude_build) return true; return false; } private static boolean isLoopingBlock(SSABlock originalBlock, THashSet handled, SSABlock block) { if(!handled.add(block)) return false; if(block == originalBlock) return true; for(SSABlock succBlock : block.getExit().getSuccessors()) if(isLoopingBlock(originalBlock, handled, succBlock)) return true; return false; } }