001/* 002 * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.lir.constopt; 024 025import java.util.*; 026 027import com.oracle.graal.debug.*; 028import com.oracle.graal.debug.Debug.*; 029 030import com.oracle.graal.compiler.common.cfg.*; 031import com.oracle.graal.lir.constopt.ConstantTree.Flags; 032import com.oracle.graal.lir.constopt.ConstantTree.NodeCost; 033 034/** 035 * Analyzes a {@link ConstantTree} and marks potential materialization positions. 036 */ 037public final class ConstantTreeAnalyzer { 038 private final ConstantTree tree; 039 private final BitSet visited; 040 041 public static NodeCost analyze(ConstantTree tree, AbstractBlockBase<?> startBlock) { 042 try (Scope s = Debug.scope("ConstantTreeAnalyzer")) { 043 ConstantTreeAnalyzer analyzer = new ConstantTreeAnalyzer(tree); 044 analyzer.analyzeBlocks(startBlock); 045 return tree.getCost(startBlock); 046 } catch (Throwable e) { 047 throw Debug.handle(e); 048 } 049 } 050 051 private ConstantTreeAnalyzer(ConstantTree tree) { 052 this.tree = tree; 053 this.visited = new BitSet(tree.size()); 054 } 055 056 /** 057 * Queues all relevant blocks for {@linkplain #process processing}. 058 * 059 * This is a worklist-style algorithm because a (more elegant) recursive implementation may 060 * cause {@linkplain StackOverflowError stack overflows} on larger graphs. 061 * 062 * @param startBlock The start block of the dominator subtree. 063 */ 064 private void analyzeBlocks(AbstractBlockBase<?> startBlock) { 065 Deque<AbstractBlockBase<?>> worklist = new ArrayDeque<>(); 066 worklist.offerLast(startBlock); 067 while (!worklist.isEmpty()) { 068 AbstractBlockBase<?> block = worklist.pollLast(); 069 try (Indent i = Debug.logAndIndent(3, "analyze: %s", block)) { 070 assert block != null : "worklist is empty!"; 071 assert isMarked(block) : "Block not part of the dominator tree: " + block; 072 073 if (isLeafBlock(block)) { 074 Debug.log(3, "leaf block"); 075 leafCost(block); 076 continue; 077 } 078 079 if (!visited.get(block.getId())) { 080 // if not yet visited (and not a leaf block) process all children first! 081 Debug.log(3, "not marked"); 082 worklist.offerLast(block); 083 List<? extends AbstractBlockBase<?>> children = block.getDominated(); 084 children.forEach(child -> filteredPush(worklist, child)); 085 visited.set(block.getId()); 086 } else { 087 Debug.log(3, "marked"); 088 // otherwise, process block 089 process(block); 090 } 091 } 092 } 093 } 094 095 /** 096 * Calculates the cost of a {@code block}. It is assumed that all {@code children} have already 097 * been {@linkplain #process processed} 098 * 099 * @param block The block to be processed. 100 */ 101 private void process(AbstractBlockBase<?> block) { 102 List<UseEntry> usages = new ArrayList<>(); 103 double bestCost = 0; 104 int numMat = 0; 105 List<? extends AbstractBlockBase<?>> children = block.getDominated(); 106 assert children.stream().anyMatch(this::isMarked) : "no children? should have called leafCost(): " + block; 107 108 // collect children costs 109 for (AbstractBlockBase<?> child : children) { 110 if (isMarked(child)) { 111 NodeCost childCost = tree.getCost(child); 112 assert childCost != null : "Child with null cost? block: " + child; 113 usages.addAll(childCost.getUsages()); 114 numMat += childCost.getNumMaterializations(); 115 bestCost += childCost.getBestCost(); 116 } 117 } 118 assert numMat > 0 : "No materialization? " + numMat; 119 120 // choose block 121 List<UseEntry> usagesBlock = tree.getUsages(block); 122 double probabilityBlock = block.probability(); 123 124 if (!usagesBlock.isEmpty() || shouldMaterializerInCurrentBlock(probabilityBlock, bestCost, numMat)) { 125 // mark current block as potential materialization position 126 usages.addAll(usagesBlock); 127 bestCost = probabilityBlock; 128 numMat = 1; 129 tree.set(Flags.CANDIDATE, block); 130 } else { 131 // stick with the current solution 132 } 133 134 assert (new HashSet<>(usages)).size() == usages.size() : "doulbe entries? " + usages; 135 NodeCost nodeCost = new NodeCost(bestCost, usages, numMat); 136 tree.setCost(block, nodeCost); 137 } 138 139 /** 140 * This is the cost function that decides whether a materialization should be inserted in the 141 * current block. 142 * <p> 143 * Note that this function does not take into account if a materialization is required despite 144 * the probabilities (e.g. there are usages in the current block). 145 * 146 * @param probabilityBlock Probability of the current block. 147 * @param probabilityChildren Accumulated probability of the children. 148 * @param numMat Number of materializations along the subtrees. We use {@code numMat - 1} to 149 * insert materializations as late as possible if the probabilities are the same. 150 */ 151 private static boolean shouldMaterializerInCurrentBlock(double probabilityBlock, double probabilityChildren, int numMat) { 152 return probabilityBlock * Math.pow(0.9, numMat - 1) < probabilityChildren; 153 } 154 155 private void filteredPush(Deque<AbstractBlockBase<?>> worklist, AbstractBlockBase<?> block) { 156 if (isMarked(block)) { 157 Debug.log(3, "adding %s to the worklist", block); 158 worklist.offerLast(block); 159 } 160 } 161 162 private void leafCost(AbstractBlockBase<?> block) { 163 tree.set(Flags.CANDIDATE, block); 164 tree.getOrInitCost(block); 165 } 166 167 private boolean isMarked(AbstractBlockBase<?> block) { 168 return tree.isMarked(block); 169 } 170 171 private boolean isLeafBlock(AbstractBlockBase<?> block) { 172 return tree.isLeafBlock(block); 173 } 174 175}