1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-09-08 17:54:40 +00:00

Implemented check in pass1 ensuring that all used vars have been assigned a value. Closes #8. Closes #21.

This commit is contained in:
jespergravgaard 2017-12-03 16:49:56 +01:00
parent b43489f11a
commit 87f58d8782
6 changed files with 97 additions and 9 deletions

View File

@ -118,6 +118,7 @@ public class Compiler {
getLog().append("INITIAL CONTROL FLOW GRAPH");
getLog().append(program.getGraph().toString(program));
new Pass1AssertUsedVars(program).execute();
new Pass1ExtractInlineStrings(program).execute();
new Pass1EliminateUncalledProcedures(program).execute();
new Pass1EliminateUnusedVars(program).execute();

View File

@ -0,0 +1,78 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
import java.util.Collection;
import java.util.LinkedHashSet;
/**
* Checks that all used variables are assigned a value before their use
*/
public class Pass1AssertUsedVars extends Pass1Base {
public Pass1AssertUsedVars(Program program) {
super(program);
}
@Override
public boolean executeStep() {
new Pass3StatementIndices(getProgram()).generateStatementIndices();
new Pass3VariableReferenceInfos(getProgram()).generateVariableReferenceInfos();
VariableReferenceInfos referenceInfos = getProgram().getVariableReferenceInfos();
ControlFlowBlock beginBlock = getProgram().getGraph().getBlock(new LabelRef(SymbolRef.BEGIN_BLOCK_NAME));
assertUsedVars(beginBlock, referenceInfos, new LinkedHashSet<>(), new LinkedHashSet<>());
getProgram().setVariableReferenceInfos(null);
new Pass3StatementIndices(getProgram()).clearStatementIndices();
return false;
}
/**
* Assert that all used vars have been assigned values before the use.
* Follow the control flow of the graph recursively.
* @param block The block to examine
* @param referenceInfos Information about assigned/used variables in statements
* @param defined Variables already assigned a value at the point of the first execution of the block
* @param visited Blocks already visited
*/
public void assertUsedVars(ControlFlowBlock block, VariableReferenceInfos referenceInfos, Collection<VariableRef> defined, Collection<LabelRef> visited) {
if(visited.contains(block.getLabel())) {
return;
}
visited.add(block.getLabel());
for (Statement statement : block.getStatements()) {
Collection<VariableRef> used = referenceInfos.getUsed(statement);
for (VariableRef usedRef : used) {
if(!defined.contains(usedRef)) {
throw new CompileError("Error! Variable used before being defined "+usedRef.toString(getProgram())+" in "+statement.toString(getProgram(), false));
}
}
Collection<VariableRef> defd = referenceInfos.getDefined(statement);
for (VariableRef definedRef : defd) {
defined.add(definedRef);
}
if(statement instanceof StatementCall) {
StatementCall call = (StatementCall) statement;
Procedure procedure = getProgram().getScope().getProcedure(call.getProcedure());
for (String paramName : procedure.getParameterNames()) {
defined.add(procedure.getVariable(paramName).getRef());
}
ControlFlowBlock procedureStart = getProgram().getGraph().getBlock(call.getProcedure().getLabelRef());
assertUsedVars(procedureStart, referenceInfos, defined, visited);
} else if(statement instanceof StatementConditionalJump) {
StatementConditionalJump cond= (StatementConditionalJump) statement;
ControlFlowBlock jumpTo = getProgram().getGraph().getBlock(cond.getDestination());
assertUsedVars(jumpTo, referenceInfos, defined, visited);
}
}
ControlFlowBlock successor = getProgram().getGraph().getBlock(block.getDefaultSuccessor());
if(successor!=null) {
assertUsedVars(successor, referenceInfos, defined, visited);
}
}
}

View File

@ -17,7 +17,6 @@ public class Pass1EliminateUnusedVars extends Pass1Base {
public boolean executeStep() {
new Pass3StatementIndices(getProgram()).generateStatementIndices();
new Pass3VariableReferenceInfos(getProgram()).generateVariableReferenceInfos();
VariableReferenceInfos referenceInfos = getProgram().getVariableReferenceInfos();
boolean modified = false;
@ -51,7 +50,6 @@ public class Pass1EliminateUnusedVars extends Pass1Base {
getProgram().setVariableReferenceInfos(null);
new Pass3StatementIndices(getProgram()).clearStatementIndices();
return modified;
}

View File

@ -32,11 +32,6 @@ public class TestErrors extends TestCase {
compileAndCompare("inline-asm-param");
}
public void testUseUninitialized() throws IOException, URISyntaxException {
String filename = "useuninitialized";
compileAndCompare(filename);
}
public void testForRangeSymbolic() throws IOException, URISyntaxException {
String filename = "forrangesymbolic";
compileAndCompare(filename);

View File

@ -287,6 +287,16 @@ public class TestPrograms extends TestCase {
fail("Expected compile error.");
}
public void testUseUninitialized() throws IOException, URISyntaxException {
try {
compileAndCompare("useuninitialized");
} catch (CompileError e) {
// expecting error!
return;
}
fail("Expected compile error.");
}
public void testTypeMismatch() throws IOException, URISyntaxException {
try {
compileAndCompare("typemismatch");

View File

@ -1,7 +1,13 @@
// Use an uninitialized variable - should fail gracefully!
// Currently it gets into the optimization phase, where the optimization runs into problems understanding the assignment/usage-graph and ends up failing on an exception
byte b;
byte s;
byte w=2;
void main() {
byte b;
byte s=b+1;
s=b+1;
b=3;
byte* screen = $0400;
*screen = b;
*(screen+1) = s;
}