1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-11-23 08:32:39 +00:00

Implemented call parameter type checking in pass 1.

This commit is contained in:
jespergravgaard 2019-06-08 15:01:44 +02:00
parent d58c46e46f
commit a180a4c0b4
7 changed files with 164 additions and 113 deletions

View File

@ -164,7 +164,7 @@ public class Compiler {
new Pass1Procedures(program).execute(); new Pass1Procedures(program).execute();
new PassNTypeInference(program).execute(); new PassNTypeInference(program).execute();
new PassNTypeIdSimplification(program).execute(); new PassNTypeIdSimplification(program).execute();
new Pass1AssertProcedureCallParameters(program).execute();
new Pass1UnwindStructValues(program).execute(); new Pass1UnwindStructValues(program).execute();
if(getLog().isVerbosePass1CreateSsa()) { if(getLog().isVerbosePass1CreateSsa()) {

View File

@ -0,0 +1,60 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.CompileError;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementAssignment;
import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.statements.StatementConditionalJump;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypeConversion;
import dk.camelot64.kickc.model.types.SymbolTypeInference;
import dk.camelot64.kickc.model.values.*;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
/**
* Asserts that all calls have parameters of the types that the procedure expects
*/
public class Pass1AssertProcedureCallParameters extends Pass1Base {
public Pass1AssertProcedureCallParameters(Program program) {
super(program);
}
@Override
public boolean step() {
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
for(Statement statement : block.getStatements()) {
if(statement instanceof StatementCall) {
StatementCall call = (StatementCall) statement;
Procedure procedure = getScope().getProcedure(call.getProcedure());
List<Variable> declParameters = procedure.getParameters();
List<RValue> callParameters = call.getParameters();
if(callParameters.size()!=declParameters.size()) {
throw new CompileError("Wrong number of parameters in call "+call.toString(getProgram(), false)+" expected "+procedure.toString(getProgram()), statement);
}
for(int i = 0; i < declParameters.size(); i++) {
Variable declParameter = declParameters.get(i);
RValue callParameter = callParameters.get(i);
SymbolType callParameterType = SymbolTypeInference.inferType(getScope(), callParameter);
SymbolType declParameterType = declParameter.getType();
if(!SymbolTypeConversion.assignmentTypeMatch(declParameterType, callParameterType)) {
throw new CompileError("Parameters type mismatch in call "+call.toString(getProgram(), false)+" expected "+procedure.toString(getProgram()), statement);
}
}
}
}
}
return false;
}
}

View File

@ -51,8 +51,9 @@ public class Pass1UnwindStructValues extends Pass1Base {
} }
} }
} }
for(Procedure procedure : getScope().getAllProcedures(true)) {
// Iterate through all procedures changing parameter lists by unwinding each struct value parameter
for(Procedure procedure : getScope().getAllProcedures(true)) {
ArrayList<String> unwoundParameterNames = new ArrayList<>(); ArrayList<String> unwoundParameterNames = new ArrayList<>();
boolean procedureUnwound = false; boolean procedureUnwound = false;
for(Variable parameter : procedure.getParameters()) { for(Variable parameter : procedure.getParameters()) {
@ -72,8 +73,7 @@ public class Pass1UnwindStructValues extends Pass1Base {
} }
} }
// Unwind all references to struct values into members
// Unwind all references to full structs
for(ControlFlowBlock block : getGraph().getAllBlocks()) { for(ControlFlowBlock block : getGraph().getAllBlocks()) {
ListIterator<Statement> stmtIt = block.getStatements().listIterator(); ListIterator<Statement> stmtIt = block.getStatements().listIterator();
while(stmtIt.hasNext()) { while(stmtIt.hasNext()) {
@ -83,44 +83,7 @@ public class Pass1UnwindStructValues extends Pass1Base {
if(assignment.getlValue() instanceof VariableRef) { if(assignment.getlValue() instanceof VariableRef) {
Variable assignedVar = getScope().getVariable((VariableRef) assignment.getlValue()); Variable assignedVar = getScope().getVariable((VariableRef) assignment.getlValue());
if(assignedVar.getType() instanceof SymbolTypeStruct) { if(assignedVar.getType() instanceof SymbolTypeStruct) {
// Assigning a struct! unwindStructAssignment(assignment, assignedVar, stmtIt, structUnwinding);
if(assignment.getOperator() == null && assignment.getrValue2() instanceof StructZero) {
// Initializing a struct - unwind to assigning zero to each member!
StructUnwinding.VariableUnwinding variableUnwinding = structUnwinding.getVariableUnwinding(assignedVar.getRef());
if(variableUnwinding != null) {
stmtIt.previous();
for(String memberName : variableUnwinding.getMemberNames()) {
VariableRef memberVarRef = variableUnwinding.getMemberUnwinding(memberName);
Variable memberVar = getScope().getVariable(memberVarRef);
Statement initStmt = Pass0GenerateStatementSequence.createDefaultInitializationStatement(memberVarRef, memberVar.getType(), statement.getSource(), Comment.NO_COMMENTS);
stmtIt.add(initStmt);
getLog().append("Adding struct value member variable default initializer " + initStmt.toString(getProgram(), false));
}
stmtIt.next();
stmtIt.remove();
}
} else if(assignment.getOperator() == null && assignment.getrValue2() instanceof VariableRef) {
Variable sourceVar = getScope().getVariable((VariableRef) assignment.getrValue2());
if(sourceVar.getType().equals(assignedVar.getType())) {
// Copying a struct - unwind to assigning each member!
StructUnwinding.VariableUnwinding assignedMemberVariables = structUnwinding.getVariableUnwinding(assignedVar.getRef());
StructUnwinding.VariableUnwinding sourceMemberVariables = structUnwinding.getVariableUnwinding(sourceVar.getRef());
if(assignedMemberVariables != null && sourceMemberVariables != null) {
stmtIt.previous();
for(String memberName : assignedMemberVariables.getMemberNames()) {
VariableRef assignedMemberVarRef = assignedMemberVariables.getMemberUnwinding(memberName);
VariableRef sourceMemberVarRef = sourceMemberVariables.getMemberUnwinding(memberName);
Statement copyStmt = new StatementAssignment(assignedMemberVarRef, sourceMemberVarRef, statement.getSource(), Comment.NO_COMMENTS);
stmtIt.add(copyStmt);
getLog().append("Adding struct value member variable copy " + copyStmt.toString(getProgram(), false));
}
stmtIt.next();
stmtIt.remove();
}
} else {
throw new CompileError("Incompatible struct assignment " + statement.toString(getProgram(), false), statement);
}
}
} }
} }
} else if(statement instanceof StatementCall) { } else if(statement instanceof StatementCall) {
@ -179,6 +142,56 @@ public class Pass1UnwindStructValues extends Pass1Base {
return modified; return modified;
} }
/**
* Unwind an assignment to a struct value variable into assignment of each member
* @param assignment The assignment statement
* @param assignedVar The struct value variable being assigned to (the LValue)
* @param stmtIt The statement iterator used for adding/removing statements
* @param structUnwinding Information about unwound struct value variables
*/
public void unwindStructAssignment(StatementAssignment assignment, Variable assignedVar, ListIterator<Statement> stmtIt, StructUnwinding structUnwinding) {
// Assigning a struct!
if(assignment.getOperator() == null && assignment.getrValue2() instanceof StructZero) {
// Initializing a struct - unwind to assigning zero to each member!
StructUnwinding.VariableUnwinding variableUnwinding = structUnwinding.getVariableUnwinding(assignedVar.getRef());
if(variableUnwinding != null) {
stmtIt.previous();
for(String memberName : variableUnwinding.getMemberNames()) {
VariableRef memberVarRef = variableUnwinding.getMemberUnwinding(memberName);
Variable memberVar = getScope().getVariable(memberVarRef);
Statement initStmt = Pass0GenerateStatementSequence.createDefaultInitializationStatement(memberVarRef, memberVar.getType(), assignment.getSource(), Comment.NO_COMMENTS);
stmtIt.add(initStmt);
getLog().append("Adding struct value member variable default initializer " + initStmt.toString(getProgram(), false));
}
stmtIt.next();
stmtIt.remove();
}
} else if(assignment.getOperator() == null && assignment.getrValue2() instanceof VariableRef) {
Variable sourceVar = getScope().getVariable((VariableRef) assignment.getrValue2());
if(sourceVar.getType().equals(assignedVar.getType())) {
// Copying a struct - unwind to assigning each member!
StructUnwinding.VariableUnwinding assignedMemberVariables = structUnwinding.getVariableUnwinding(assignedVar.getRef());
StructUnwinding.VariableUnwinding sourceMemberVariables = structUnwinding.getVariableUnwinding(sourceVar.getRef());
if(assignedMemberVariables != null && sourceMemberVariables != null) {
stmtIt.previous();
for(String memberName : assignedMemberVariables.getMemberNames()) {
VariableRef assignedMemberVarRef = assignedMemberVariables.getMemberUnwinding(memberName);
VariableRef sourceMemberVarRef = sourceMemberVariables.getMemberUnwinding(memberName);
Statement copyStmt = new StatementAssignment(assignedMemberVarRef, sourceMemberVarRef, assignment.getSource(), Comment.NO_COMMENTS);
stmtIt.add(copyStmt);
getLog().append("Adding struct value member variable copy " + copyStmt.toString(getProgram(), false));
}
stmtIt.next();
stmtIt.remove();
}
} else {
throw new CompileError("Incompatible struct assignment " + assignment.toString(getProgram(), false), assignment);
}
} else {
throw new CompileError("Incompatible struct assignment " + assignment.toString(getProgram(), false), assignment);
}
}
/** /**
* Keeps track of all structs that have been unwound into member variables. * Keeps track of all structs that have been unwound into member variables.
*/ */

View File

@ -51,43 +51,53 @@ public class TestPrograms {
} }
@Test @Test
public void testError1() throws IOException, URISyntaxException { public void testStructError3() throws IOException, URISyntaxException {
assertError("struct-err-3", "Parameters type mismatch in call");
}
@Test
public void testStructError2() throws IOException, URISyntaxException {
assertError("struct-err-2", "Incompatible struct assignment");
}
@Test
public void testStructError1() throws IOException, URISyntaxException {
assertError("struct-err-1", "Incompatible struct assignment"); assertError("struct-err-1", "Incompatible struct assignment");
} }
@Test @Test
public void testError0() throws IOException, URISyntaxException { public void testStructError0() throws IOException, URISyntaxException {
assertError("struct-err-0", "Unknown struct type"); assertError("struct-err-0", "Unknown struct type");
} }
@Test @Test
public void testStruct5() throws IOException, URISyntaxException { public void testStruct5() throws IOException, URISyntaxException {
compileAndCompare("struct-5", log()); compileAndCompare("struct-5");
} }
@Test @Test
public void testStruct4() throws IOException, URISyntaxException { public void testStruct4() throws IOException, URISyntaxException {
compileAndCompare("struct-4", log()); compileAndCompare("struct-4");
} }
@Test @Test
public void testStruct3() throws IOException, URISyntaxException { public void testStruct3() throws IOException, URISyntaxException {
compileAndCompare("struct-3", log().verboseCreateSsa()); compileAndCompare("struct-3");
} }
@Test @Test
public void testStruct2() throws IOException, URISyntaxException { public void testStruct2() throws IOException, URISyntaxException {
compileAndCompare("struct-2", log()); compileAndCompare("struct-2");
} }
@Test @Test
public void testStruct1() throws IOException, URISyntaxException { public void testStruct1() throws IOException, URISyntaxException {
compileAndCompare("struct-1", log()); compileAndCompare("struct-1");
} }
@Test @Test
public void testStruct0() throws IOException, URISyntaxException { public void testStruct0() throws IOException, URISyntaxException {
compileAndCompare("struct-0", log()); compileAndCompare("struct-0");
} }
@Test @Test

View File

@ -0,0 +1,14 @@
// Incompatible struct assignment
struct Point {
byte x;
byte y;
};
void main() {
struct Point p1;
p1 = 4;
const byte* SCREEN = 0x0400;
SCREEN[0] = p1.x;
}

View File

@ -0,0 +1,17 @@
// Incompatible struct parameter
struct Point {
byte x;
byte y;
};
void main() {
print(7);
}
const byte* SCREEN = 0x0400;
void print(struct Point p) {
*SCREEN = p.x;
}

View File

@ -14,70 +14,7 @@ Replacing struct member reference (struct Point) main::p1.y with member variable
Replacing struct member reference (struct Point) main::p1.x with member variable reference (byte) main::p1_x Replacing struct member reference (struct Point) main::p1.x with member variable reference (byte) main::p1_x
Replacing struct member reference (struct Point) print::p.x with member variable reference (byte) print::p_x Replacing struct member reference (struct Point) print::p.x with member variable reference (byte) print::p_x
Replacing struct member reference (struct Point) print::p.y with member variable reference (byte) print::p_y Replacing struct member reference (struct Point) print::p.y with member variable reference (byte) print::p_y
SYMBOLS
(label) @1
(label) @2
(label) @begin
(label) @end
(byte) Point::x
(byte) Point::y
(byte*) SCREEN
(byte) idx
(void()) main()
(void~) main::$0
(void~) main::$1
(label) main::@return
(struct Point) main::p1
(byte) main::p1_x
(byte) main::p1_y
(void()) print((byte) print::p_x , (byte) print::p_y)
(label) print::@return
(struct Point) print::p
(byte) print::p_x
(byte) print::p_y
Adding pointer type conversion cast (byte*) SCREEN in (byte*) SCREEN ← (number) $400 Adding pointer type conversion cast (byte*) SCREEN in (byte*) SCREEN ← (number) $400
INITIAL CONTROL FLOW GRAPH
@begin: scope:[] from
to:@1
main: scope:[main] from
[0] (byte) main::p1_x ← (byte) 0
[1] (byte) main::p1_y ← (byte) 0
[2] (byte) main::p1_x ← (number) 1
[3] (byte) main::p1_y ← (number) 4
[4] (void~) main::$0 ← call print (byte) main::p1_x (byte) main::p1_y
[5] (byte) main::p1_x ← (number) 2
[6] (void~) main::$1 ← call print (byte) main::p1_x (byte) main::p1_y
to:main::@return
main::@return: scope:[main] from main
[7] return
to:@return
@1: scope:[] from @begin
[8] (byte*) SCREEN ← ((byte*)) (number) $400
[9] (byte) idx ← (number) 0
to:@2
print: scope:[print] from
[10] *((byte*) SCREEN + (byte) idx) ← (byte) print::p_x
[11] (byte) idx ← ++ (byte) idx
[12] *((byte*) SCREEN + (byte) idx) ← (byte) print::p_y
[13] (byte) idx ← ++ (byte) idx
to:print::@return
print::@return: scope:[print] from print
[14] return
to:@return
@2: scope:[] from @1
[15] call main
to:@end
@end: scope:[] from @2
Eliminating unused variable - keeping the call (void~) main::$0
Eliminating unused variable - keeping the call (void~) main::$1
PROCEDURE MODIFY VARIABLE ANALYSIS
main modifies idx
print modifies idx
Completing Phi functions...
Completing Phi functions...
CONTROL FLOW GRAPH SSA CONTROL FLOW GRAPH SSA
@begin: scope:[] from @begin: scope:[] from