mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-11-14 23:04:57 +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:
parent
b43489f11a
commit
87f58d8782
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user