/** * 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 de.codesourcery.jasm16.ast.ASTNode; import de.codesourcery.jasm16.ast.ASTUtils; import de.codesourcery.jasm16.ast.ASTVisitor; import de.codesourcery.jasm16.ast.IIterationContext; import de.codesourcery.jasm16.ast.ISimpleASTNodeVisitor; import de.codesourcery.jasm16.ast.OperandNode; import de.codesourcery.jasm16.ast.OperatorNode; import de.codesourcery.jasm16.ast.RegisterReferenceNode; import de.codesourcery.jasm16.ast.TermNode; import de.codesourcery.jasm16.compiler.CompilationError; import de.codesourcery.jasm16.compiler.CompilationWarning; import de.codesourcery.jasm16.compiler.CompilerPhase; import de.codesourcery.jasm16.compiler.ICompilationContext; import de.codesourcery.jasm16.compiler.ICompilationUnit; import de.codesourcery.jasm16.compiler.ICompiler.CompilerOption; import de.codesourcery.jasm16.compiler.ICompilerPhase; import de.codesourcery.jasm16.compiler.IMarker; import de.codesourcery.jasm16.parser.Operator; /** * AST validation phase that makes sure that all literal/address values values * are within the bounds of the DCPU-16 architecture. * * <p>This phase requires expressions to already be folded/reduced to literal values * and labels resolved to their memory addresses so it can only run after * the {@link FoldExpressionsPhase}.</p> * * @author tobias.gierke@code-sourcery.de */ public class ASTValidationPhase2 extends CompilerPhase { public ASTValidationPhase2() { super(ICompilerPhase.PHASE_VALIDATE_AST2); } protected static class RegisterWithOffsetValidator implements ISimpleASTNodeVisitor<ASTNode> { private int registerRefCount=0; private ICompilationUnit unit; private ICompilationContext context; public RegisterWithOffsetValidator(ICompilationContext context) { this.unit = context.getCurrentCompilationUnit(); this.context = context; } @Override public boolean visit(ASTNode node) { if ( node instanceof RegisterReferenceNode) { registerRefCount++; if ( registerRefCount > 1 ) { unit.addMarker( new CompilationError("Expression must not contain more than one register reference",unit,node) ); } if ( node.getParent() != null && node.getParent() instanceof OperatorNode) { final Operator operator = ((OperatorNode) node.getParent()).getOperator(); if ( operator != Operator.PLUS && operator != Operator.MINUS ) { unit.addMarker( new CompilationError("Register-indirect with offset did not evaluate to an addition",unit,node) ); } } } else if ( node instanceof OperatorNode) { return checkValueInRange( context , (OperatorNode) node ); } return true; } }; @Override protected void run(final ICompilationUnit unit , final ICompilationContext compContext) throws IOException { if ( unit.getAST() == null ) { return; } final ASTVisitor visitor = new ASTVisitor() { @Override public void visit(OperandNode node, IIterationContext context) { switch( node.getAddressingMode() ) { case IMMEDIATE: if ( node.getChildCount() != 1 ) { unit.addMarker( new CompilationError("Addressing operand with != one child ?",unit,node) ); } final ISimpleASTNodeVisitor<ASTNode> visitor = new ISimpleASTNodeVisitor<ASTNode>() { @Override public boolean visit(ASTNode node) { if ( node instanceof TermNode) { return checkTermNode( compContext, (TermNode) node); } return true; } }; ASTUtils.visitInOrder( node , visitor ); break; case INDIRECT: if ( node.getChildCount() != 1 ) { unit.addMarker( new CompilationError("Addressing operand with != one child ?",unit,node) ); } final ISimpleASTNodeVisitor<ASTNode> visitor2 = new ISimpleASTNodeVisitor<ASTNode>() { @Override public boolean visit(ASTNode node) { if ( node instanceof OperatorNode) { return checkValueInRange( compContext , (OperatorNode) node ); } return true; } }; ASTUtils.visitInOrder( node , visitor2 ); break; case INDIRECT_REGISTER_OFFSET: ASTUtils.visitInOrder( node , new RegisterWithOffsetValidator( compContext ) ); break; } } }; ASTUtils.visitInOrder( unit.getAST() , visitor ); } private static boolean checkValueInRange(final ICompilationContext compContext,OperatorNode node) { final ICompilationUnit unit = compContext.getCurrentCompilationUnit(); final boolean[] valueInRange = {true}; final ISimpleASTNodeVisitor<ASTNode> visitor = new ISimpleASTNodeVisitor<ASTNode>() { @Override public boolean visit(ASTNode node) { if ( node instanceof TermNode) { final TermNode op = (TermNode) node; final Long longValue = op.calculate( compContext.getSymbolTable() ); if ( longValue == null ) { unit.addMarker( new CompilationError("Internal error, operand value has no value?",unit,node) ); valueInRange[0]=false; return false; } final boolean isInRange; if ( longValue.longValue() < 0 ) { isInRange = longValue >= -32768 && longValue <= 32767; } else { // value is >= 0 isInRange = longValue <= 65535; } if ( ! isInRange ) { final IMarker marker; if ( ! compContext.hasCompilerOption( CompilerOption.RELAXED_VALIDATION ) ) { marker=new CompilationError("Operand value "+longValue+" out-of-range(does not fit in 16 bits)",unit,node); } else { marker= new CompilationWarning("Operand value "+longValue+" out-of-range(does not fit in 16 bits)",unit,node); } unit.addMarker( marker ); valueInRange[0]=false; return false; } } return true; } }; ASTUtils.visitInOrder( node , visitor ); return valueInRange[0]; } private boolean checkTermNode(final ICompilationContext compContext,TermNode op) { final ICompilationUnit unit = compContext.getCurrentCompilationUnit(); final Long longValue = op.calculate( compContext.getSymbolTable() ); if ( longValue == null ) { unit.addMarker( new CompilationError("Internal error, operand value has no value?",unit,op) ); return false; } final boolean isInRange; if ( longValue.longValue() < 0 ) { isInRange = longValue >= -32768 && longValue <= 65535; } else { // value is >= 0 isInRange = longValue <= 65535; } if ( ! isInRange ) { final IMarker marker; if ( ! compContext.hasCompilerOption( CompilerOption.RELAXED_VALIDATION ) ) { marker=new CompilationError("Operand value "+longValue+" out-of-range(does not fit in 16 bits)",unit,op); } else { marker= new CompilationWarning("Operand value "+longValue+" out-of-range(does not fit in 16 bits)",unit,op); } unit.addMarker( marker ); return false; } return true; } protected boolean isAbortOnErrors() { return true; } }