blob: 90f89014cda78064d6dc1802910e1cbeda8f3a19 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 University of Illinois at Urbana-Champaign and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* UIUC - Initial API and implementation
*******************************************************************************/
package org.eclipse.photran.internal.core.refactoring;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.photran.internal.core.analysis.binding.Definition;
import org.eclipse.photran.internal.core.analysis.binding.ScopingNode;
import org.eclipse.photran.internal.core.analysis.loops.ASTProperLoopConstructNode;
import org.eclipse.photran.internal.core.analysis.loops.ASTVisitorWithLoops;
import org.eclipse.photran.internal.core.analysis.loops.LoopReplacer;
import org.eclipse.photran.internal.core.lexer.Terminal;
import org.eclipse.photran.internal.core.lexer.Token;
import org.eclipse.photran.internal.core.parser.ASTIntConstNode;
import org.eclipse.photran.internal.core.parser.ASTVisitor;
import org.eclipse.photran.internal.core.parser.IASTListNode;
import org.eclipse.photran.internal.core.parser.IASTNode;
import org.eclipse.photran.internal.core.parser.IExecutionPartConstruct;
import org.eclipse.photran.internal.core.parser.IExpr;
import org.eclipse.photran.internal.core.refactoring.infrastructure.FortranEditorRefactoring;
import org.eclipse.photran.internal.core.reindenter.Reindenter;
import org.eclipse.photran.internal.core.reindenter.Reindenter.Strategy;
import org.eclipse.rephraserengine.core.refactorings.UserInputString;
/**
* Loop refactoring to take a nested loop and change the data accesses to a tiled form.
* @author Ashley Kasza
*/
public class TileLoopRefactoring extends FortranEditorRefactoring
{
private ASTProperLoopConstructNode doLoop;
private ASTProperLoopConstructNode secondDoLoop;
private String newFirstIndexVar;
private String newSecondIndexVar;
private int tilingSize;
private int tilingOffset;
private boolean hasLoopDependency;
@Override
protected void doCheckInitialConditions(RefactoringStatus status, IProgressMonitor pm)
throws PreconditionFailure
{
ensureProjectHasRefactoringEnabled(status);
LoopReplacer.replaceAllLoopsIn(this.astOfFileInEditor.getRoot());
doLoop = getLoopNode(this.astOfFileInEditor, this.selectedRegionInEditor);
// fail if no do loop was selected
if (doLoop == null)
{
fail(Messages.ReverseLoopRefactoring_SelectDoLoop);
}
secondDoLoop = findSecondDoLoop(doLoop);
if(secondDoLoop == null){
fail(Messages.ReverseLoopRefactoring_SelectDoLoop);
}
//check for nested loops.
if(findNumberOfLoops() != 1){
fail(Messages.TileLoopRefactoring_SelectLoopWithOnlyOneNestedLoop);
}
newFirstIndexVar = findNewIndexVariableName(doLoop, doLoop.getIndexVariable().getText());
newSecondIndexVar = findNewIndexVariableName(secondDoLoop, secondDoLoop.getIndexVariable().getText());
if(newFirstIndexVar == null || newSecondIndexVar == null){
fail(Messages.TileLoopRefactoring_UnableToCreateNewIndex);
}
try{
if(doLoop.getStepInt() != 1 || secondDoLoop.getStepInt() != 1){
fail(Messages.TileLoopRefactoring_CantTileLoopsWithStep);
}
}catch(NumberFormatException e){
fail(Messages.TileLoopRefactoring_CantTileLoopsWithStep);
}
}
private ASTProperLoopConstructNode findSecondDoLoop(ASTProperLoopConstructNode firstLoop){
IASTListNode<IExecutionPartConstruct> firstBody = firstLoop.getBody();
return firstBody.findFirst(ASTProperLoopConstructNode.class);
}
private String findNewIndexVariableName(ASTProperLoopConstructNode nodeInScope, String indexVar){
boolean canUse;
String newIndexVar = indexVar;
for (int i = 1; i <= 10; i++)
{
canUse = true;
ScopingNode scope = ScopingNode.getLocalScope(nodeInScope);
List<Definition> defList = scope.getAllDefinitions();
for (Definition d : defList)
{
if (d != null && d.getCanonicalizedName().equals(newIndexVar.toLowerCase()))
{
newIndexVar = indexVar + Integer.toString(i);
canUse = false;
}
}
if (canUse == true)
{
return newIndexVar;
}
}
return null;
}
private int findNumberOfLoops(){
int loopCount = 0;
IASTListNode<IExecutionPartConstruct> doLoopBody = doLoop.getBody();
for(int i = 0; i < doLoopBody.size(); i++){
if(doLoopBody.get(i) instanceof ASTProperLoopConstructNode){
loopCount++;
}
}
return loopCount;
}
@Override
protected void doCheckFinalConditions(RefactoringStatus status, IProgressMonitor pm)
throws PreconditionFailure
{
// no final conditions
if(tilingSize <= 0){
fail(Messages.TileLoopRefactoring_InvalidTileSize);
}
if(tilingOffset <0){
fail(Messages.TileLoopRefactoring_InvalidTilingOffset);
}
}
@Override
protected void doCreateChange(IProgressMonitor pm) throws CoreException,
OperationCanceledException
{
ScopingNode scope = ScopingNode.getLocalScope(doLoop);
hasLoopDependency = false;
findIndexVariableInHeader(secondDoLoop.getLoopHeader(), doLoop.getIndexVariable().getText());
declareNewLoopVariables(scope);
@SuppressWarnings("unused")
ASTProperLoopConstructNode node = loopTilingTransformation();
//parseLiteralDoLoop(node.toString());
//scope = ScopingNode.getLocalScope(node);
//Reindenter.reindent(loopTilingTransformation(), this.astOfFileInEditor, Strategy.REINDENT_EACH_LINE);
Reindenter.reindent(scope.getBody(), this.astOfFileInEditor, Strategy.REINDENT_EACH_LINE);
this.addChangeFromModifiedAST(this.fileInEditor, pm);
vpg.releaseAST(this.fileInEditor);
}
private void findIndexVariableInHeader(IASTNode node, final String indexVariable){
node.accept(new ASTVisitorWithLoops()
{
@Override public void visitToken(Token token)
{
if(token.getTerminal() == Terminal.T_IDENT && (token.getText()).equalsIgnoreCase(indexVariable)){
hasLoopDependency = true;
}
}
});
//hasLoopDependency = false;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private ASTProperLoopConstructNode loopTilingTransformation(){
ASTProperLoopConstructNode firstElementLoop = constructNewElementLoop(doLoop, newFirstIndexVar); //inner loop
ASTProperLoopConstructNode firstTileLoop = createNewTileLoop(doLoop, newFirstIndexVar);
ASTProperLoopConstructNode secondElementLoop = constructNewElementLoop(secondDoLoop, newSecondIndexVar);
ASTProperLoopConstructNode secondTileLoop = createNewTileLoop(secondDoLoop, newSecondIndexVar);
if(hasLoopDependency){
replaceBound(secondTileLoop.getLowerBoundIExpr(), doLoop.getLowerBoundIExpr().toString(), firstTileLoop.getIndexVariable().getText(), true);
replaceBound(secondTileLoop.getUpperBoundIExpr(), doLoop.getUpperBoundIExpr().toString(), firstTileLoop.getIndexVariable().getText(), false);
}
IASTListNode body;
body = secondElementLoop.getBody();
body.add(secondDoLoop.getBody());
body = firstElementLoop.getBody();
body.add(secondElementLoop);
body = secondTileLoop.getBody();
body.add(firstElementLoop);
body = firstTileLoop.getBody();
body.add(secondTileLoop);
doLoop.replaceWith(firstTileLoop);
return firstTileLoop;
}
private void replaceBound(IExpr expr, String replace1, String replace2, boolean isLb){
if(!(expr instanceof ASTIntConstNode))
{
IExpr dummyBound = (IExpr)(expr.clone());
IExpr dummyBound2 = (IExpr)(expr.clone());
replaceFirstLoopIndexVariable(dummyBound, doLoop.getIndexVariable().getText(), replace1);
replaceFirstLoopIndexVariable(dummyBound2, doLoop.getIndexVariable().getText(), replace2);
if(!(dummyBound.toString().equals(dummyBound2.toString()))){
String replacementBound;
if(isLb == true)
replacementBound = String.format("max(%s,%s)", dummyBound.toString(), dummyBound2.toString()); //$NON-NLS-1$
else
replacementBound = String.format("min(%s,%s)", dummyBound.toString(), dummyBound2.toString()); //$NON-NLS-1$
expr.replaceWith(replacementBound);
}
}
}
private void replaceFirstLoopIndexVariable(IASTNode node, final String indexVar, final String replacement)
{
node.accept(new ASTVisitor()
{
@Override public void visitToken(Token token)
{
if(token.getTerminal() == Terminal.T_IDENT && token.getText().equalsIgnoreCase(indexVar)){
token.replaceWith(replacement);
}
}
});
}
@SuppressWarnings("unchecked")
private void declareNewLoopVariables(ScopingNode scope){
//ScopingNode scope = ScopingNode.getLocalScope(doLoop);
IASTListNode<IASTNode> scopeBody = (IASTListNode<IASTNode>)scope.getOrCreateBody();
String declarationString = "integer :: " + newFirstIndexVar + ", " + newSecondIndexVar; //$NON-NLS-1$ //$NON-NLS-2$
int insertionIndex = findIndexToInsertTypeDeclaration(scopeBody);
scopeBody.add(insertionIndex, parseLiteralStatement(declarationString));
}
private ASTProperLoopConstructNode createNewTileLoop(ASTProperLoopConstructNode inputLoop, String newIndexVar){
String newLb = getNewBoundsString(inputLoop.getLowerBoundIExpr());
String newUb = getNewBoundsString(inputLoop.getUpperBoundIExpr());
String loopHeader = String.format("do %s=%s,%s,%s\nend do\n", newIndexVar, //$NON-NLS-1$
newLb, newUb,tilingSize);
return parseLiteralDoLoop(loopHeader);
}
private String getNewBoundsString(IExpr expr){
if(expr instanceof ASTIntConstNode){
int loopBoundInt = Integer.parseInt(expr.toString());
int newBound = (int)Math.floor((double)(loopBoundInt-tilingOffset)/tilingSize);
newBound = (newBound * tilingSize)+tilingOffset;
return Integer.toString(newBound);
}else{
//interchangeWithIdentifier = true;
return String.format("floor(real(%s-%s)/%s)*%s+%s",expr.toString(), Integer.toString(tilingOffset), Integer.toString(tilingSize), //$NON-NLS-1$
Integer.toString(tilingSize), Integer.toString(tilingOffset));
}
}
private ASTProperLoopConstructNode constructNewElementLoop(ASTProperLoopConstructNode inputLoop, String newIndexVar){
String elementLoopLb = String.format("max(%s,%s)", inputLoop.getLowerBoundIExpr().toString(), newIndexVar);//$NON-NLS-1$
String elementLoopUb = String.format("min(%s,%s+%s)", inputLoop.getUpperBoundIExpr().toString(), newIndexVar, (tilingSize-1));//$NON-NLS-1$
String elementLoopFull = String.format("do %s=%s,%s\n end do\n", inputLoop.getIndexVariable().getText(), elementLoopLb, elementLoopUb); //$NON-NLS-1$
ASTProperLoopConstructNode newNode = parseLiteralDoLoop(elementLoopFull);
return newNode;
}
@UserInputString(label = "Enter tile size ", defaultValueMethod= "getSuggestedTilingSize")
public void setLoopTilingStepNumber(String input)
{
tilingSize = Integer.parseInt(input);
}
public String getSuggestedTilingSize()
{
return "1"; //$NON-NLS-1$
}
@UserInputString(label = "Enter tile offset ", defaultValueMethod = "getSuggestedTilingOffset")
public void setLoopTilingOffsetNumber(String input)
{
tilingOffset = Integer.parseInt(input);
}
public String getSuggestedTilingOffset()
{
return "1"; //$NON-NLS-1$
}
@Override
public String getName()
{
return Messages.TileLoopRefactoring_LoopTilingName;
}
}