1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-11-26 12:49:21 +00:00

Fixed passing of value lists as return values. Fixed problems with clobbering in return/call return value fetch.

This commit is contained in:
jespergravgaard 2020-03-03 01:25:02 +01:00
parent 2427720581
commit 2c177dd45f
10 changed files with 128 additions and 61 deletions

View File

@ -1,4 +0,0 @@
lda #<{c2}
sta {c1}
lda #>{c2}
sta {c1}+1

View File

@ -1,4 +0,0 @@
lda #<{c2}
sta {c1}
lda #>{c2}
sta {c1}+1

View File

@ -265,6 +265,8 @@ public class Compiler {
//getLog().append(program.getGraph().toString(program));
new Pass1ProcedureCallParameters(program).generate();
//getLog().append("CONTROL FLOW GRAPH (BEFORE LIST UNWINDING)");
//getLog().append(program.getGraph().toString(program));
new PassNUnwindLValueLists(program).execute();
//new Pass1PointifyMemoryVariables(program).execute();
@ -277,6 +279,8 @@ public class Compiler {
//getLog().append(program.getGraph().toString(program));
program.setGraph(new Pass1ProcedureCallsReturnValue(program).generate());
//getLog().append("CONTROL FLOW GRAPH (BEFORE LIST UNWINDING)");
//getLog().append(program.getGraph().toString(program));
new PassNUnwindLValueLists(program).execute();
getLog().append("\nCONTROL FLOW GRAPH SSA");

View File

@ -5,7 +5,6 @@ import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.iterator.ProgramValueIterator;
import dk.camelot64.kickc.model.values.RangeValue;
import dk.camelot64.kickc.model.values.Value;
import dk.camelot64.kickc.model.values.ValueList;
/**
* Assert that all intermediate RValues in the code have been replaced in pass 2.
@ -23,14 +22,6 @@ public class Pass3AssertRValues extends Pass2SsaAssertion {
public void check() throws AssertionFailed {
ProgramValueIterator.execute(getGraph(), (programValue, currentStmt, stmtIt, currentBlock) -> {
Value value = programValue.get();
if(value instanceof ValueList) {
throw new InternalError(
"Error! Value list not resolved to word constructor or array initializer" +
"\n value list: " + value.toString(getProgram()) +
"\n statement: " + currentStmt.toString(getProgram(), false)
, currentStmt.getSource()
);
}
if(value instanceof RangeValue) {
throw new InternalError(
"Error! Ranged for() not resolved to constants" +

View File

@ -4,12 +4,11 @@ import dk.camelot64.kickc.asm.AsmChunk;
import dk.camelot64.kickc.asm.AsmClobber;
import dk.camelot64.kickc.asm.AsmProgram;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementCallPrepare;
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
import dk.camelot64.kickc.model.statements.*;
import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.RValue;
import dk.camelot64.kickc.model.values.ValueList;
import dk.camelot64.kickc.model.values.VariableRef;
import java.util.ArrayList;
@ -85,37 +84,62 @@ public class Pass4AssertNoCpuClobber extends Pass2Base {
PhiTransitions phiTransitions = programPhiTransitions.get(statementBlock.getLabel());
PhiTransitions.PhiTransition phiTransition = phiTransitions.getTransition(phiTransitionId);
for(PhiTransitions.PhiTransition.PhiAssignment phiAssignment : phiTransition.getAssignments()) {
if(phiAssignment.getAssignmentIdx() != transitionAssignmentIdx) {
// Only the current transition var is assigned
VariableRef assignedVar = phiAssignment.getVariable();
assignedVars.remove(assignedVar);
}
if(phiAssignment.getAssignmentIdx() > transitionAssignmentIdx) {
// IF the assignment is later than the current one
// For all assignments later than the current one - add referenced vars to alive
RValue rValue = phiAssignment.getrValue();
Collection<VariableRef> alive = VariableReferenceInfos.getReferencedVars(rValue);
aliveVars.addAll(alive);
VariableRef assignedVar = phiAssignment.getVariable();
assignedVars.remove(assignedVar);
alive.remove(assignedVar);
} else if(phiAssignment.getAssignmentIdx() < transitionAssignmentIdx) {
// IF the assignment is before the current one
VariableRef assignedVar = phiAssignment.getVariable();
assignedVars.remove(assignedVar);
}
}
}
// If the chunk is an call parameter prepare, examine the later call parameter prepares and update alive variables
if(statement instanceof StatementCallPrepare && asmChunk.getSubStatementId() != null && asmChunk.getSubStatementIdx() != null) {
int transitionAssignmentIdx = asmChunk.getSubStatementIdx();
int parameterIdx = asmChunk.getSubStatementIdx();
final StatementCallPrepare callPrepare = (StatementCallPrepare) statement;
final int numParameters = callPrepare.getNumParameters();
for(int idx = 0; idx < numParameters; idx++) {
for(int idx = parameterIdx + 1; idx < numParameters; idx++) {
// For parameter prepares later than the current one - add referenced to alive
final RValue parameter = callPrepare.getParameter(idx);
if(idx > transitionAssignmentIdx) {
// If the parameter prepare is later than the current one
Collection<VariableRef> alive = VariableReferenceInfos.getReferencedVars(parameter);
aliveVars.addAll(alive);
Collection<VariableRef> alive = VariableReferenceInfos.getReferencedVars(parameter);
aliveVars.addAll(alive);
}
}
// If the chunk is call finalize with a value list LValue, examine the LValue assigns and update alive variables
if(statement instanceof StatementCallFinalize && asmChunk.getSubStatementId() != null && asmChunk.getSubStatementIdx() != null) {
int memberIdx = asmChunk.getSubStatementIdx();
final StatementCallFinalize stmtCallFinalize = (StatementCallFinalize) statement;
final List<RValue> lValues = ((ValueList)stmtCallFinalize.getlValue()).getList();
final int numLValues = lValues.size();
for(int idx = 0; idx < numLValues; idx++) {
if(idx != memberIdx) {
// Only the current transition var is assigned
final VariableRef assignedVar = (VariableRef) lValues.get(idx);
assignedVars.remove(assignedVar);
}
}
}
// If the chunk is call with a value list LValue, examine the later LValue assigns and update alive variables
if(statement instanceof StatementReturn && asmChunk.getSubStatementId() != null && asmChunk.getSubStatementIdx() != null) {
int memberIdx = asmChunk.getSubStatementIdx();
final StatementReturn stmtReturn = (StatementReturn) statement;
final List<RValue> returnValues = ((ValueList) stmtReturn.getValue()).getList();
final int numReturnValues = returnValues.size();
for(int idx = memberIdx + 1; idx < numReturnValues; idx++) {
// Add all referenced vars in later return values to alive
final RValue returnValue = returnValues.get(idx);
Collection<VariableRef> alive = VariableReferenceInfos.getReferencedVars(returnValue);
aliveVars.addAll(alive);
}
}
// Non-assigned alive variables must not be clobbered
for(VariableRef aliveVar : aliveVars) {
Variable variable = getProgram().getSymbolInfos().getVariable(aliveVar);

View File

@ -896,12 +896,33 @@ public class Pass4CodeGeneration {
}
// Pull result from the stack
if(call.getlValue() != null) {
if(stackCleanBytes>0)
if(stackCleanBytes > 0)
asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo));
SymbolType returnType = procedure.getReturnType();
AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(call.getlValue(), new StackPullValue(returnType), program, block.getScope());
ensureEncoding(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec());
boolean isValueListStruct = (call.getlValue() instanceof ValueList) && (procedure.getReturnType() instanceof SymbolTypeStruct);
if(!isValueListStruct) {
// Simple return value - fetch from stack
SymbolType returnType = procedure.getReturnType();
AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(call.getlValue(), new StackPullValue(returnType), program, block.getScope());
ensureEncoding(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec());
} else {
// Struct value list return value - fetch each member from stack
final List<RValue> lValues = ((ValueList) call.getlValue()).getList();
final StructDefinition structDefinition = ((SymbolTypeStruct) procedure.getReturnType()).getStructDefinition(getScope());
final List<Variable> memberVars = new ArrayList<>(structDefinition.getAllVars(false));
for(int i = 0; i < memberVars.size(); i++) {
LValue lValue = (LValue) lValues.get(i);
Variable memberDef = memberVars.get(i);
final SymbolType memberType = memberDef.getType();
if(i > 0)
asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo));
AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(lValue, new StackPullValue(memberType), program, block.getScope());
ensureEncoding(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec());
asm.getCurrentChunk().setSubStatementIdx(i);
asm.getCurrentChunk().setSubStatementId(memberDef.toString(program));
}
}
}
}
} else if(statement instanceof StatementReturn) {
@ -915,13 +936,36 @@ public class Pass4CodeGeneration {
StatementReturn returnStatement = (StatementReturn) statement;
if(returnStatement.getValue() != null) {
// Store return value on stack
SymbolType returnType = procedure.getReturnType();
// Find parameter/return stack size
ConstantRef returnOffsetConstant = CallingConventionStack.getReturnOffsetConstant(procedure);
AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(new StackIdxValue(returnOffsetConstant, returnType), returnStatement.getValue(), program, block.getScope());
asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo));
ensureEncoding(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec());
SymbolType returnType = procedure.getReturnType();
boolean isValueListStruct = (returnStatement.getValue() instanceof ValueList) && (returnType instanceof SymbolTypeStruct);
if(!isValueListStruct) {
// A simple return type - put it on the stack
ConstantRef returnOffsetConstant = CallingConventionStack.getReturnOffsetConstant(procedure);
AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(new StackIdxValue(returnOffsetConstant, returnType), returnStatement.getValue(), program, block.getScope());
ensureEncoding(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec());
} else {
// A struct return value in a value list - Store each value on the stack
final List<RValue> returnValues = ((ValueList) returnStatement.getValue()).getList();
final StructDefinition structDefinition = ((SymbolTypeStruct) returnType).getStructDefinition(getScope());
final List<Variable> memberVars = new ArrayList<>(structDefinition.getAllVars(false));
for(int i = 0; i < memberVars.size(); i++) {
RValue returnValue = returnValues.get(i);
Variable memberDef = memberVars.get(i);
final SymbolType memberType = memberDef.getType();
ConstantRef returnOffsetConstant = CallingConventionStack.getReturnOffsetConstant(procedure);
ConstantRef structMemberOffsetConstant = SizeOfConstants.getStructMemberOffsetConstant(getScope(), structDefinition, memberDef.getLocalName());
ConstantBinary memberReturnOffsetConstant = new ConstantBinary(returnOffsetConstant, Operators.PLUS, structMemberOffsetConstant);
if(i > 0)
asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo));
AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(new StackIdxValue(memberReturnOffsetConstant, memberType), returnValue, program, block.getScope());
ensureEncoding(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec());
asm.getCurrentChunk().setSubStatementIdx(i);
asm.getCurrentChunk().setSubStatementId(memberDef.toString(program));
}
}
}
}

View File

@ -5,6 +5,8 @@ import dk.camelot64.kickc.model.statements.StatementAssignment;
import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.statements.StatementCallFinalize;
import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.values.RValue;
import dk.camelot64.kickc.model.values.ValueList;
import dk.camelot64.kickc.model.values.VariableRef;
import java.util.ArrayList;
@ -114,32 +116,32 @@ public class Pass4LiveRangeEquivalenceClassesFinalize extends Pass2Base {
@Override
public Void visitAssignment(StatementAssignment assignment) {
if(assignment.getlValue() instanceof VariableRef) {
VariableRef lValVar = (VariableRef) assignment.getlValue();
List<VariableRef> preferences = new ArrayList<>();
addToEquivalenceClassSet(lValVar, preferences, liveRangeEquivalenceClassSet);
}
addValueToEquivalenceClassSet(assignment.getlValue());
return null;
}
@Override
public Void visitCall(StatementCall call) {
if(call.getlValue() instanceof VariableRef) {
VariableRef lValVar = (VariableRef) call.getlValue();
List<VariableRef> preferences = new ArrayList<>();
addToEquivalenceClassSet(lValVar, preferences, liveRangeEquivalenceClassSet);
}
addValueToEquivalenceClassSet(call.getlValue());
return null;
}
@Override
public Void visitCallFinalize(StatementCallFinalize callFinalize) {
if(callFinalize.getlValue() instanceof VariableRef) {
VariableRef lValVar = (VariableRef) callFinalize.getlValue();
addValueToEquivalenceClassSet(callFinalize.getlValue());
return null;
}
private void addValueToEquivalenceClassSet(RValue lValue) {
if(lValue instanceof VariableRef) {
VariableRef lValVar = (VariableRef) lValue;
List<VariableRef> preferences = new ArrayList<>();
addToEquivalenceClassSet(lValVar, preferences, liveRangeEquivalenceClassSet);
} else if(lValue instanceof ValueList) {
for(RValue rValue : ((ValueList) lValue).getList()) {
addValueToEquivalenceClassSet(rValue);
}
}
return null;
}
}

View File

@ -7,6 +7,8 @@ import dk.camelot64.kickc.fragment.AsmFragmentTemplateSynthesizer;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.statements.*;
import dk.camelot64.kickc.model.values.LValue;
import dk.camelot64.kickc.model.values.RValue;
import dk.camelot64.kickc.model.values.ValueList;
import dk.camelot64.kickc.model.values.VariableRef;
import java.util.*;
@ -39,13 +41,21 @@ public class Pass4RegisterUpliftPotentialRegisterAnalysis extends Pass2Base {
return variableRefs;
} else if(statement instanceof StatementLValue) {
LValue lValue = ((StatementLValue) statement).getlValue();
if(lValue instanceof VariableRef) {
variableRefs.add((VariableRef) lValue);
}
addAssignedVars(lValue, variableRefs);
}
return variableRefs;
}
private static void addAssignedVars(RValue lValue, LinkedHashSet<VariableRef> variableRefs) {
if(lValue instanceof VariableRef) {
variableRefs.add((VariableRef) lValue);
} else if(lValue instanceof ValueList) {
for(RValue rValue : ((ValueList) lValue).getList()) {
addAssignedVars(rValue, variableRefs);
}
}
}
/***
* For each statement - try out all potential register combinations and examine the clobber
*