compiler-design-eth/src/cd/backend/codegen/CompileTimeChecks.java

107 lines
3.3 KiB
Java
Executable File

package cd.backend.codegen;
import cd.Config;
import cd.backend.ExitCode;
import cd.ir.Ast;
import cd.ir.Ast.*;
import cd.ir.CompileTimeEvaluator;
import cd.ir.Symbol.VariableSymbol;
import cd.transform.analysis.MaybeC;
import java.util.Optional;
import static cd.backend.codegen.AssemblyEmitter.constant;
import static cd.backend.codegen.RegisterManager.STACK_REG;
/**
* Class with simple static checks mimicking the runtime system with the functions to check
* possible errors at runtime. The purpose is to skip unnecessary calls to those functions
*/
public class CompileTimeChecks {
protected final CfgCodeGenerator cfgCg;
protected final AstCodeGeneratorRef cgRef;
protected CompileTimeEvaluator cte;
public CompileTimeChecks(AstCodeGeneratorRef cg, CfgCodeGenerator cfgCodeGenerator) {
this.cfgCg = cfgCodeGenerator;
this.cgRef = cg;
cte = new CompileTimeEvaluator();
}
/**
* Convenience method that extracts the VariableSymbol from a Var or Field node.
* <br/> If the node is not one of those, the node must be a Index
*/
private VariableSymbol symbolOf(Expr expr) {
if (expr instanceof Var)
return ((Var) expr).sym;
if (expr instanceof Field)
return ((Field) expr).sym;
// assert expr instanceof Index // only for local checks
return null;
}
/** Returns whether a NewArray expression needs to check dynamically for the
* validity of the index (>=0). In case that it is known but invalid, a call
* to exit is done with the appropriate error code.
*/
public boolean checkArraySize(Ast.NewArray expr) {
Optional<Integer> arraySize = cte.calc(expr.arg());
if (!arraySize.isPresent()) {
return true;
}
int arraySizeValue = arraySize.get();
if (arraySizeValue < 0) {
cgRef.emit.emitStore(constant(ExitCode.INVALID_ARRAY_SIZE.value), 0, STACK_REG);
cgRef.emit.emit("call", Config.EXIT);
return false;
}
return false;
}
/** Returns whether a run-time check for the bounds of an array index is necessary.
* <br/> A check is only necessary if the index expression cannot be evaluated in
* compile time or the size of the array is unknown.
* <br/> If the value is known and it is invalid, an unconditional call to exit is
* performed with the appropriate exit code
*/
public boolean checkArrayBound(Index expr) {
Optional<Integer> index = cte.calc(expr.right());
if (!index.isPresent()) {
return true;
}
VariableSymbol symbol = symbolOf(expr.left());
if (!cfgCg.arraySizeState.containsKey(symbol)) {
return true;
}
int indexValue = index.get();
MaybeC<Integer> bound = cfgCg.arraySizeState.get(symbol);
if (bound.isNotConstant()) {
return true;
}
int boundValue = bound.getValue();
if (indexValue < 0 || indexValue > (boundValue - 1)) {
cgRef.emit.emitStore(constant(ExitCode.INVALID_ARRAY_BOUNDS.value), 0, STACK_REG);
cgRef.emit.emit("call", Config.EXIT);
}
return false;
}
/**
* Returns true only when it is impossible for the expression (Field or Var) to be null.
* <br/> Currently doesn't check indexes.
*/
public boolean isNotNull(Expr expr) {
if (expr instanceof ThisRef) {
return false;
} else {
VariableSymbol symbol = symbolOf(expr);
MaybeC<Boolean> isNull = cfgCg.nullState.get(symbol);
return isNull != null && isNull.isConstant() && !isNull.getValue();
}
}
}