/**
* Copyright 2012 Tobias Gierke <tobias.gierke@code-sourcery.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.codesourcery.jasm16.compiler.phases;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Stack;
import org.apache.log4j.Logger;
import de.codesourcery.jasm16.Address;
import de.codesourcery.jasm16.ByteAddress;
import de.codesourcery.jasm16.ISymbolAware;
import de.codesourcery.jasm16.Size;
import de.codesourcery.jasm16.WordAddress;
import de.codesourcery.jasm16.ast.ASTNode;
import de.codesourcery.jasm16.ast.IncludeSourceFileNode;
import de.codesourcery.jasm16.ast.LabelNode;
import de.codesourcery.jasm16.ast.ObjectCodeOutputNode;
import de.codesourcery.jasm16.compiler.CompilerPhase;
import de.codesourcery.jasm16.compiler.DebugInfo;
import de.codesourcery.jasm16.compiler.ICompilationContext;
import de.codesourcery.jasm16.compiler.ICompilationListener;
import de.codesourcery.jasm16.compiler.ICompilationUnit;
import de.codesourcery.jasm16.compiler.ICompilationUnitResolver;
import de.codesourcery.jasm16.compiler.ICompiler.CompilerOption;
import de.codesourcery.jasm16.compiler.ICompilerPhase;
import de.codesourcery.jasm16.compiler.IParentSymbolTable;
import de.codesourcery.jasm16.compiler.Label;
import de.codesourcery.jasm16.compiler.SourceLocation;
import de.codesourcery.jasm16.compiler.io.IObjectCodeWriterFactory;
import de.codesourcery.jasm16.compiler.io.IResource;
import de.codesourcery.jasm16.compiler.io.IResourceResolver;
/**
* This compiler phase calculates the addresses of labels defined in the source code.
*
* @author tobias.gierke@code-sourcery.de
*/
public class CalculateAddressesPhase extends CompilerPhase {
private static final Logger LOG = Logger.getLogger(CalculateAddressesPhase.class);
public CalculateAddressesPhase() {
super(ICompilerPhase.PHASE_RESOLVE_ADDRESSES);
}
@Override
public boolean execute(final List<ICompilationUnit> units,
final DebugInfo debugInfo,
final IParentSymbolTable symbolTable ,
final IObjectCodeWriterFactory writerFactory ,
final ICompilationListener listener,
final IResourceResolver resourceResolver,
final Set<CompilerOption> options, final ICompilationUnitResolver compUnitResolver)
{
/*
* Since the size of an instruction depends on the
* size of its operands and operands may include expressions
* that refer to labels, the literal value of an expression
* may change when the addresses of labels change their value
* and vice versa.
*
* This method recalculates label addresses as long as the size
* of the generated object code is not stable (=does not change any longer
* because all expressions evaluated to a value <=1f or >1f, read: their
* values can or cannot be inlined into the instruction itself)
*/
final Map<String,Long> sizeByCompilationUnit = new HashMap<String,Long>();
final DebugInfo debugInfoToUpdate = options.contains(CompilerOption.GENERATE_DEBUG_INFO) ? debugInfo : null;
final ICompilationContextFactory factory = new ICompilationContextFactory() {
@Override
public ICompilationContext createContext(ICompilationUnit unit)
{
return createCompilationContext(units, symbolTable, writerFactory, resourceResolver, options,compUnitResolver,unit);
}
};
boolean sizeIsStable;
do
{
sizeIsStable = true;
if ( debugInfoToUpdate != null ) {
debugInfoToUpdate.clear();
}
Address currentOffset = ByteAddress.ZERO;
for ( final ICompilationUnit unit : units )
{
if ( unit.getAST() == null )
{
continue;
}
final ICompilationContext context = factory.createContext( unit );
final long newSizeInBytes = assignAddresses( context , unit , debugInfoToUpdate , currentOffset , factory );
// System.out.println("Objectcode size for "+unit.getResource()+" is now: "+newSizeInBytes+" bytes");
Long oldSizeInBytes = sizeByCompilationUnit.get( unit.getIdentifier() );
if ( oldSizeInBytes == null ) {
sizeByCompilationUnit.put( unit.getIdentifier() , newSizeInBytes );
sizeIsStable = false;
} else {
sizeByCompilationUnit.put( unit.getIdentifier() , newSizeInBytes );
if ( oldSizeInBytes.longValue() != newSizeInBytes ) {
sizeIsStable = false;
}
}
currentOffset = currentOffset.plus( Size.bytes( (int) newSizeInBytes ) , false );
}
} while ( ! sizeIsStable );
return true;
}
protected interface ICompilationContextFactory {
public ICompilationContext createContext(ICompilationUnit unit);
}
private int assignAddresses(final ICompilationContext compContext,
final ICompilationUnit currentUnit,
final DebugInfo debugInfo ,
final Address startingOffset,
final ICompilationContextFactory contextFactory)
{
final MyVisitor first = new MyVisitor(compContext,currentUnit,debugInfo,startingOffset);
final FancyVisitor visitor = new FancyVisitor() {
protected MyVisitor current;
private final Stack<MyVisitor> stack = new Stack<MyVisitor>() {
@Override
public MyVisitor push(MyVisitor item)
{
final MyVisitor result = super.push(item);
current = item;
return result;
}
@Override
public synchronized MyVisitor pop()
{
MyVisitor result = super.pop();
current = peek();
return result;
}
};
{
stack.push(first);
}
@Override
public void visit(ASTNode node)
{
current.visit( node );
}
@Override
public void beforeDescent(ASTNode node)
{
if ( node instanceof IncludeSourceFileNode)
{
final IResource resource = ((IncludeSourceFileNode) node).getResource();
final ICompilationUnit newUnit;
try {
newUnit = compContext.getCompilationUnit( resource );
} catch (IOException e) {
throw new RuntimeException(e);
}
if ( newUnit == null ) {
throw new RuntimeException("Failed to locate compilation unit for resource "+resource );
}
final ICompilationContext newContext = contextFactory.createContext( newUnit );
stack.push( new MyVisitor(newContext, newUnit , debugInfo , Address.byteAddress( stack.peek().getCurrentByteOffset() ) ) );
}
}
@Override
public void afterDescent(ASTNode node)
{
if ( node instanceof IncludeSourceFileNode) {
MyVisitor previous = stack.pop();
stack.peek().incOffset( previous.getSizeInBytes() );
}
}
};
visitPostOrder( currentUnit.getAST() , visitor );
return first.getSizeInBytes();
}
protected final class MyVisitor implements FancyVisitor
{
private long currentByteOffset;
private final ICompilationUnit currentUnit;
private final DebugInfo debugInfo;
private final Address startingOffset;
private final ICompilationContext compContext;
public MyVisitor(final ICompilationContext compContext,
final ICompilationUnit currentUnit,
final DebugInfo debugInfo , Address startingOffset)
{
this.compContext = compContext;
this.startingOffset = startingOffset;
this.currentByteOffset = startingOffset.getByteAddressValue();
this.debugInfo = debugInfo;
this.currentUnit = currentUnit;
}
public int getSizeInBytes() {
return (int) (currentByteOffset - startingOffset.getByteAddressValue());
}
public void incOffset(int bytes) {
currentByteOffset+=bytes;
}
public long getCurrentByteOffset()
{
return currentByteOffset;
}
@Override
public void visit(ASTNode n)
{
try {
internalVisit( n );
} catch(RuntimeException e) {
LOG.error("visit(): Failed to assign addresses to "+n+" at "+currentUnit+" ( "+n.getTextRegion()+") ");
throw e;
}
}
public void internalVisit(ASTNode n)
{
// System.out.println("---> [ "+currentUnit.getResource()+"] visiting "+n.getClass().getSimpleName()+" ("+n+")");
if ( n instanceof IncludeSourceFileNode )
{
// already handled by parent visitor
}
else if ( n instanceof LabelNode)
{
final Label symbol = ((LabelNode) n).getLabel();
if ( symbol != null )
{
long byteAddress = currentByteOffset;
int wordAddress = (int) (byteAddress >> 1);
if ( ( wordAddress << 1 ) != byteAddress ) {
throw new RuntimeException("Internal error, address of label "+symbol+" is "+
byteAddress+" which is not on a 16-bit boundary?");
}
final WordAddress labelAddress = Address.wordAddress( wordAddress ) ;
// System.out.println("Assigning "+labelAddress+" to label "+symbol.getIdentifier() );
symbol.setAddress( labelAddress );
}
}
else if ( n instanceof ObjectCodeOutputNode)
{
final ObjectCodeOutputNode outputNode = (ObjectCodeOutputNode) n;
if ( n instanceof ISymbolAware ) {
outputNode.symbolsResolved( compContext );
}
if ( debugInfo != null && compContext.hasCompilerOption( CompilerOption.GENERATE_DEBUG_INFO ) )
{
final long byteAddress = currentByteOffset;
final int wordAddress = (int) (byteAddress >> 1);
if ( ( wordAddress << 1 ) != byteAddress ) {
throw new RuntimeException("Internal error, address of instruction "+outputNode+" is "+
byteAddress+" which is not on a 16-bit boundary?");
}
final WordAddress address = Address.wordAddress( wordAddress ) ;
SourceLocation sourceLocation = null;
try {
sourceLocation = currentUnit.getSourceLocation( outputNode.getTextRegion() );
}
catch(NoSuchElementException e) {
final String msg = "Failed to find source location for node "+outputNode+" with "+outputNode.getTextRegion();
final NoSuchElementException ex = new NoSuchElementException( msg );
throw ex;
}
debugInfo.addSourceLocation( address, sourceLocation );
}
final int sizeInBytes = outputNode.getSizeInBytes( currentByteOffset );
if ( sizeInBytes != ObjectCodeOutputNode.UNKNOWN_SIZE )
{
currentByteOffset += sizeInBytes;
}
} else if ( n instanceof ISymbolAware ) {
((ISymbolAware) n).symbolsResolved( compContext );
}
}
@Override
public void beforeDescent(ASTNode node)
{
}
@Override
public void afterDescent(ASTNode node)
{
}
}
protected interface FancyVisitor {
public void beforeDescent(ASTNode node);
public void visit(ASTNode node);
public void afterDescent(ASTNode node);
}
private static void visitPostOrder(ASTNode node,FancyVisitor visitor)
{
visitPostOrder( node ,0 , visitor );
}
private static void visitPostOrder(ASTNode node, int depth , FancyVisitor visitor)
{
int currentDepth = depth;
for ( ASTNode child : node.getChildren() )
{
visitor.beforeDescent( child );
visitPostOrder( child , currentDepth+1, visitor );
visitor.afterDescent( child );
}
visitor.visit( node );
}
@Override
protected void run(ICompilationUnit unit, ICompilationContext context)
throws IOException {
throw new UnsupportedOperationException("Not implemented");
}
}