diff --git a/src/main/java/dk/camelot64/kickc/model/StructUnwinding.java b/src/main/java/dk/camelot64/kickc/model/StructUnwinding.java index 960bd9b30..c7477fde0 100644 --- a/src/main/java/dk/camelot64/kickc/model/StructUnwinding.java +++ b/src/main/java/dk/camelot64/kickc/model/StructUnwinding.java @@ -4,7 +4,10 @@ import dk.camelot64.kickc.model.symbols.ArraySpec; import dk.camelot64.kickc.model.symbols.ProgramScope; import dk.camelot64.kickc.model.symbols.StructDefinition; import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.types.SymbolTypeStruct; import dk.camelot64.kickc.model.values.*; +import dk.camelot64.kickc.passes.unwinding.RValueUnwinding; +import dk.camelot64.kickc.passes.unwinding.StructMemberUnwinding; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -70,7 +73,7 @@ public class StructUnwinding { public RValueUnwinding getMemberUnwinding(String memberName) { return new RValueUnwinding() { @Override - public SymbolType getType() { + public SymbolType getSymbolType() { return structDefinition.getMember(memberName).getType(); } @@ -85,7 +88,18 @@ public class StructUnwinding { } @Override - public RValue getArrayUnwinding(ProgramScope scope, ConstantValue arraySize) { + public boolean isBulkCopyable() { + return getArraySpec()!=null || getSymbolType() instanceof SymbolTypeStruct; + } + + @Override + public LValue getBulkLValue(ProgramScope scope) { + final RValue unwinding = getUnwinding(scope); + return new PointerDereferenceSimple(unwinding); + } + + @Override + public RValue getBulkRValue(ProgramScope scope) { throw new RuntimeException("TODO: Implement!"); } }; @@ -93,59 +107,4 @@ public class StructUnwinding { } - /** Information about how members of an struct Lvalue is unwound. */ - public interface StructMemberUnwinding { - - /** - * Get the names of the members of the struct - * - * @return the names - */ - List getMemberNames(); - - /** - * Get the RValue unwinding to use for copying a single member of the struct - * - * @param memberName The member name - * @return The unwinding of the member - */ - RValueUnwinding getMemberUnwinding(String memberName); - - } - - /** - * Unwinding value used for copying a value from one variable to another. - */ - public interface RValueUnwinding { - - /** - * Get the type of the value - * @return The type of the value - */ - SymbolType getType(); - - /** - * Get the array nature of the value - * @return The array nature of the value - */ - ArraySpec getArraySpec(); - - /** - * Get the RValue to use in the assignment as LValue - and as RValue if the member is a not an array value - * - * @param programScope The program scope - * @return The unwinding of the member - */ - RValue getUnwinding(ProgramScope programScope); - - /** - * Get Rvalue to use when for copying/setting an array value. Typically returns memset/memcpy commands. - * @param scope The program scope - * @param arraySize The declared size of the array - * @return The value to use as RValue - */ - RValue getArrayUnwinding(ProgramScope scope, ConstantValue arraySize); - - } - } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureCallParameters.java b/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureCallParameters.java index b6233c8b0..5ad835b6d 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureCallParameters.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureCallParameters.java @@ -9,6 +9,7 @@ import dk.camelot64.kickc.model.symbols.*; import dk.camelot64.kickc.model.types.SymbolType; import dk.camelot64.kickc.model.types.SymbolTypeStruct; import dk.camelot64.kickc.model.values.*; +import dk.camelot64.kickc.passes.unwinding.RValueUnwinding; import java.util.*; @@ -80,7 +81,7 @@ public class Pass1ProcedureCallParameters extends ControlFlowGraphCopyVisitor { if(returnVarUnwinding!=null) { ArrayList unwoundReturnVars = new ArrayList<>(); for(String memberName : returnVarUnwinding.getMemberNames()) { - StructUnwinding.RValueUnwinding memberUnwinding = returnVarUnwinding.getMemberUnwinding(memberName); + RValueUnwinding memberUnwinding = returnVarUnwinding.getMemberUnwinding(memberName); unwoundReturnVars.add(memberUnwinding.getUnwinding(getScope())); } procReturnVarRef = new ValueList(unwoundReturnVars); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java b/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java index a3601e45a..925cd31e5 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java @@ -3,18 +3,15 @@ package dk.camelot64.kickc.passes; import dk.camelot64.kickc.model.InternalError; import dk.camelot64.kickc.model.*; import dk.camelot64.kickc.model.iterator.ProgramValueIterator; -import dk.camelot64.kickc.model.operators.OperatorSizeOf; -import dk.camelot64.kickc.model.operators.Operators; import dk.camelot64.kickc.model.statements.*; import dk.camelot64.kickc.model.symbols.*; import dk.camelot64.kickc.model.types.SymbolType; import dk.camelot64.kickc.model.types.SymbolTypeInference; -import dk.camelot64.kickc.model.types.SymbolTypePointer; import dk.camelot64.kickc.model.types.SymbolTypeStruct; import dk.camelot64.kickc.model.values.*; +import dk.camelot64.kickc.passes.unwinding.*; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.ListIterator; import java.util.concurrent.atomic.AtomicBoolean; @@ -72,10 +69,9 @@ public class Pass1UnwindStructValues extends Pass1Base { if(programValue.get() instanceof StructMemberRef) { StructMemberRef structMemberRef = (StructMemberRef) programValue.get(); if(structMemberRef.getStruct() instanceof VariableRef) { - StructUnwinding.StructMemberUnwinding memberVariables = getStructMemberUnwinding(structMemberRef.getStruct(), currentStmt, stmtIt, currentBlock); + StructMemberUnwinding memberVariables = getStructMemberUnwinding(structMemberRef.getStruct(), currentStmt, stmtIt, currentBlock); if(memberVariables != null && memberVariables != POSTPONE_UNWINDING) { - //RValue structMemberVariable = memberVariables.getMemberUnwinding(structMemberRef.getMemberName(), getScope()); - StructUnwinding.RValueUnwinding memberUnwinding = memberVariables.getMemberUnwinding(structMemberRef.getMemberName()); + RValueUnwinding memberUnwinding = memberVariables.getMemberUnwinding(structMemberRef.getMemberName()); RValue structMemberVariable = memberUnwinding.getUnwinding(getScope()); getLog().append("Replacing struct member reference " + structMemberRef.toString(getProgram()) + " with member unwinding reference " + structMemberVariable.toString(getProgram())); programValue.set(structMemberVariable); @@ -95,11 +91,11 @@ public class Pass1UnwindStructValues extends Pass1Base { private boolean unwindCall(StatementCall call, ListIterator stmtIt, ControlFlowBlock currentBlock) { // Unwind struct value return value boolean lvalUnwound = false; - StructUnwinding.StructMemberUnwinding lValueUnwinding = getStructMemberUnwinding(call.getlValue(), call, stmtIt, currentBlock); + StructMemberUnwinding lValueUnwinding = getStructMemberUnwinding(call.getlValue(), call, stmtIt, currentBlock); if(lValueUnwinding != null && lValueUnwinding != POSTPONE_UNWINDING) { ArrayList unwoundMembers = new ArrayList<>(); for(String memberName : lValueUnwinding.getMemberNames()) { - StructUnwinding.RValueUnwinding memberUnwinding = lValueUnwinding.getMemberUnwinding(memberName); + RValueUnwinding memberUnwinding = lValueUnwinding.getMemberUnwinding(memberName); unwoundMembers.add(memberUnwinding.getUnwinding(getScope())); } ValueList unwoundLValue = new ValueList(unwoundMembers); @@ -113,11 +109,11 @@ public class Pass1UnwindStructValues extends Pass1Base { boolean anyParameterUnwound = false; for(RValue parameter : call.getParameters()) { boolean unwound = false; - StructUnwinding.StructMemberUnwinding parameterUnwinding = getStructMemberUnwinding(parameter, call, stmtIt, currentBlock); + StructMemberUnwinding parameterUnwinding = getStructMemberUnwinding(parameter, call, stmtIt, currentBlock); if(parameterUnwinding != null && parameterUnwinding != POSTPONE_UNWINDING) { // Passing a struct variable - convert it to member variables for(String memberName : parameterUnwinding.getMemberNames()) { - StructUnwinding.RValueUnwinding memberUnwinding = parameterUnwinding.getMemberUnwinding(memberName); + RValueUnwinding memberUnwinding = parameterUnwinding.getMemberUnwinding(memberName); unwoundParameters.add(memberUnwinding.getUnwinding(getScope())); } unwound = true; @@ -144,11 +140,11 @@ public class Pass1UnwindStructValues extends Pass1Base { private boolean unwindReturn(StatementReturn statementReturn, ListIterator stmtIt, ControlFlowBlock currentBlock) { boolean modified = false; // Unwind struct value return value - StructUnwinding.StructMemberUnwinding returnVarUnwinding = getStructMemberUnwinding(statementReturn.getValue(), statementReturn, stmtIt, currentBlock); + StructMemberUnwinding returnVarUnwinding = getStructMemberUnwinding(statementReturn.getValue(), statementReturn, stmtIt, currentBlock); if(returnVarUnwinding != null && returnVarUnwinding != POSTPONE_UNWINDING) { ArrayList unwoundMembers = new ArrayList<>(); for(String memberName : returnVarUnwinding.getMemberNames()) { - StructUnwinding.RValueUnwinding memberUnwinding = returnVarUnwinding.getMemberUnwinding(memberName); + RValueUnwinding memberUnwinding = returnVarUnwinding.getMemberUnwinding(memberName); unwoundMembers.add(memberUnwinding.getUnwinding(getScope())); } ValueList unwoundReturnValue = new ValueList(unwoundMembers); @@ -174,7 +170,7 @@ public class Pass1UnwindStructValues extends Pass1Base { StructUnwinding structUnwinding = getProgram().getStructUnwinding(); StructUnwinding.VariableUnwinding parameterUnwinding = structUnwinding.getVariableUnwinding(parameter.getRef()); for(String memberName : parameterUnwinding.getMemberNames()) { - StructUnwinding.RValueUnwinding memberUnwinding = parameterUnwinding.getMemberUnwinding(memberName); + RValueUnwinding memberUnwinding = parameterUnwinding.getMemberUnwinding(memberName); SymbolVariableRef memberUnwindingRef = (SymbolVariableRef) memberUnwinding.getUnwinding(getScope()); unwoundParameterNames.add(memberUnwindingRef.getLocalName()); procedureUnwound = true; @@ -202,55 +198,31 @@ public class Pass1UnwindStructValues extends Pass1Base { private boolean unwindAssignment(StatementAssignment assignment, ListIterator stmtIt, ControlFlowBlock currentBlock) { LValue lValue = assignment.getlValue(); SymbolType lValueType = SymbolTypeInference.inferType(getScope(), lValue); - if(assignment.getOperator() == null && lValueType instanceof SymbolTypeStruct) { + if(lValueType instanceof SymbolTypeStruct && assignment.getOperator() == null) { + // Assignment to a struct SymbolTypeStruct lValueStructType = (SymbolTypeStruct) lValueType; RValue rValue = assignment.getrValue2(); boolean initialAssignment = assignment.isInitialAssignment(); StatementSource source = assignment.getSource(); - // Check for constant struct value assignment - if(rValue instanceof ConstantValue && 1 == 0) { - // TODO: Only handle __ma structs here! - if(rValue instanceof StructZero) { - // Zero-fill the struct value - stmtIt.previous(); - ConstantValue structSize = OperatorSizeOf.getSizeOfConstantVar(getScope(), lValueStructType); - MemsetValue rValueMemset = new MemsetValue(structSize); - Statement copyStmt = new StatementAssignment(lValue, rValueMemset, initialAssignment, source, Comment.NO_COMMENTS); - stmtIt.add(copyStmt); - getLog().append("Adding struct variable zero-fill " + copyStmt.toString(getProgram(), false)); - stmtIt.next(); - } else if(rValue instanceof ConstantStructValue) { - // Create global constant - and memcpy the value into the variable - // Create a constant variable holding the array - String constName = getScope().allocateIntermediateVariableName(); - Variable constVar = Variable.createConstant(constName, lValueType, getScope(), null, (ConstantValue) rValue, Scope.SEGMENT_DATA_DEFAULT); - getScope().add(constVar); - stmtIt.previous(); - ConstantValue structSize = OperatorSizeOf.getSizeOfConstantVar(getScope(), lValueStructType); - MemcpyValue rValueMemcpy = new MemcpyValue(new PointerDereferenceSimple(constVar.getRef()), structSize); - Statement copyStmt = new StatementAssignment(lValue, rValueMemcpy, initialAssignment, source, Comment.NO_COMMENTS); - stmtIt.add(copyStmt); - getLog().append("Adding struct variable copy " + copyStmt.toString(getProgram(), false)); - stmtIt.next(); - } else { - throw new InternalError("Unknown constant struct value " + rValue); - } - return true; + // Check for bulk assignable values + if(isBulkAssignable(lValue) && isBulkAssignable(rValue)) { + RValueUnwinding lValueUnwinding = getValueUnwinding(lValue, assignment, stmtIt, currentBlock); + RValueUnwinding rValueUnwinding = getValueUnwinding(rValue, assignment, stmtIt, currentBlock); + unwindAssignment(lValueUnwinding, rValueUnwinding, null, stmtIt, initialAssignment, source); } // Check for struct unwinding - StructUnwinding.StructMemberUnwinding lValueUnwinding = getStructMemberUnwinding(lValue, assignment, stmtIt, currentBlock); + StructMemberUnwinding lValueUnwinding = getStructMemberUnwinding(lValue, assignment, stmtIt, currentBlock); if(lValueUnwinding == null) return false; if(lValueUnwinding == POSTPONE_UNWINDING) return true; - if(rValue instanceof StructUnwoundPlaceholder) return false; SymbolType rValueType = SymbolTypeInference.inferType(getScope(), rValue); if(rValueType.equals(lValueStructType)) { - StructUnwinding.StructMemberUnwinding rValueUnwinding = getStructMemberUnwinding(rValue, assignment, stmtIt, currentBlock); + StructMemberUnwinding rValueUnwinding = getStructMemberUnwinding(rValue, assignment, stmtIt, currentBlock); if(rValueUnwinding == null) { throw new CompileError("Incompatible struct assignment " + assignment.toString(getProgram(), false), assignment); } @@ -258,8 +230,8 @@ public class Pass1UnwindStructValues extends Pass1Base { return true; List lValueUnwoundPlaceholder = new ArrayList<>(); for(String memberName : lValueUnwinding.getMemberNames()) { - StructUnwinding.RValueUnwinding lValueMemberUnwinding = lValueUnwinding.getMemberUnwinding(memberName); - StructUnwinding.RValueUnwinding rValueMemberUnwinding = rValueUnwinding.getMemberUnwinding(memberName); + RValueUnwinding lValueMemberUnwinding = lValueUnwinding.getMemberUnwinding(memberName); + RValueUnwinding rValueMemberUnwinding = rValueUnwinding.getMemberUnwinding(memberName); unwindAssignment(lValueMemberUnwinding, rValueMemberUnwinding, lValueUnwoundPlaceholder, stmtIt, initialAssignment, source); } StructUnwoundPlaceholder unwoundPlaceholder = new StructUnwoundPlaceholder(lValueStructType, lValueUnwoundPlaceholder); @@ -285,17 +257,19 @@ public class Pass1UnwindStructValues extends Pass1Base { * @param initialAssignment Is this the initial assignment * @param source The statement source */ - private void unwindAssignment(StructUnwinding.RValueUnwinding lValueUnwinding, StructUnwinding.RValueUnwinding rValueUnwinding, List lValueUnwoundList, ListIterator stmtIt, boolean initialAssignment, StatementSource source) { - if(lValueUnwinding.getArraySpec() != null) { - // Unwinding an array struct member + private void unwindAssignment(RValueUnwinding lValueUnwinding, RValueUnwinding rValueUnwinding, List lValueUnwoundList, ListIterator stmtIt, boolean initialAssignment, StatementSource source) { + if(lValueUnwinding.isBulkCopyable() && rValueUnwinding.isBulkCopyable()) { + // Use bulk unwinding for a struct member that is an array stmtIt.previous(); - RValue lValueMemberVarPointer = lValueUnwinding.getUnwinding(getScope()); - LValue lValueMemberVarRef = new PointerDereferenceSimple(lValueMemberVarPointer); - ConstantValue arraySize = lValueUnwinding.getArraySpec().getArraySize(); - RValue rValueArrayUnwinding = rValueUnwinding.getArrayUnwinding(getScope(), arraySize); + //RValue lValueMemberVarPointer = lValueUnwinding.getBulkLValue(getScope()); + //LValue lValueMemberVarRef = new PointerDereferenceSimple(lValueMemberVarPointer); + //if(rValueUnwinding.getArraySpec()==null || !lValueUnwinding.getArraySpec().equals(rValueUnwinding.getArraySpec())) + // throw new RuntimeException("ArraySpec mismatch!"); + LValue lValueMemberVarRef = lValueUnwinding.getBulkLValue(getScope()); + RValue rValueBulkUnwinding = rValueUnwinding.getBulkRValue(getScope()); if(lValueUnwoundList != null) - lValueUnwoundList.add(lValueMemberVarPointer); - Statement copyStmt = new StatementAssignment(lValueMemberVarRef, rValueArrayUnwinding, initialAssignment, source, Comment.NO_COMMENTS); + lValueUnwoundList.add(lValueMemberVarRef); + Statement copyStmt = new StatementAssignment(lValueMemberVarRef, rValueBulkUnwinding, initialAssignment, source, Comment.NO_COMMENTS); stmtIt.add(copyStmt); stmtIt.next(); getLog().append("Adding struct value member variable copy " + copyStmt.toString(getProgram(), false)); @@ -313,6 +287,61 @@ public class Pass1UnwindStructValues extends Pass1Base { } } + /** + * Determine whether a value can be used in a bulk assignment + * + * @param value The value + * @return true if the value is bulk assignable + */ + private boolean isBulkAssignable(RValue value) { + /* + if(value instanceof SymbolVariableRef) { + Variable var = getScope().getVar((SymbolVariableRef) value); + if(var.isStructClassic()) + // A load/store struct value + return true; + } + if(value instanceof StructZero) + // A zero-filled struct value + return true; + if(value instanceof ConstantStructValue) + // A constant struct value + return true; + // TODO: Add support for arrays + // Not bulk assignable + + */ + return false; + } + + /** + * Get unwinding for a value + * + * @param value The value + * @return Unwinding for the value + */ + private RValueUnwinding getValueUnwinding(RValue value, Statement currentStmt, ListIterator stmtIt, ControlFlowBlock currentBlock) { + if(value != null) { + SymbolType valueType = SymbolTypeInference.inferType(getScope(), value); + if(valueType instanceof SymbolTypeStruct) { + if(value instanceof VariableRef) { + Variable variable = getScope().getVariable((VariableRef) value); + if(variable.isStructClassic()) { + return new StructVariableValueUnwinding(variable); + } + } + if(value instanceof StructZero) { + return new ZeroValueUnwinding(valueType, null); + } + if(value instanceof ConstantStructValue) { + return new ConstantValueUnwinding(valueType, null, (ConstantStructValue) value); + } + } + } + return null; + } + + /** * Examine a value - and if it represents a struct get the unwinding information for the struct members * @@ -322,7 +351,7 @@ public class Pass1UnwindStructValues extends Pass1Base { * @param currentBlock Program Context information. Current block * @return null if the value is not a struct. Unwinding for the passed value if it is a struct. {@link #POSTPONE_UNWINDING} if the struct is not ready for unwinding yet. */ - private StructUnwinding.StructMemberUnwinding getStructMemberUnwinding(RValue value, Statement currentStmt, ListIterator stmtIt, ControlFlowBlock currentBlock) { + private StructMemberUnwinding getStructMemberUnwinding(RValue value, Statement currentStmt, ListIterator stmtIt, ControlFlowBlock currentBlock) { if(value != null) { SymbolType valueType = SymbolTypeInference.inferType(getScope(), value); if(valueType instanceof SymbolTypeStruct) { @@ -366,7 +395,7 @@ public class Pass1UnwindStructValues extends Pass1Base { } /** Singleton signaling that unwinding should be postponed. */ - private static final StructUnwinding.StructMemberUnwinding POSTPONE_UNWINDING = new StructUnwinding.StructMemberUnwinding() { + private static final StructMemberUnwinding POSTPONE_UNWINDING = new StructMemberUnwinding() { @Override public List getMemberNames() { @@ -374,359 +403,10 @@ public class Pass1UnwindStructValues extends Pass1Base { } @Override - public StructUnwinding.RValueUnwinding getMemberUnwinding(String memberName) { + public RValueUnwinding getMemberUnwinding(String memberName) { return null; } }; - /** Unwinding for a simple pointer deref to a struct. */ - private static class StructMemberUnwindingPointerDerefSimple implements StructUnwinding.StructMemberUnwinding { - private final StructDefinition structDefinition; - private final ControlFlowBlock currentBlock; - private final ListIterator stmtIt; - private final PointerDereferenceSimple pointerDeref; - private final Statement currentStmt; - - StructMemberUnwindingPointerDerefSimple(PointerDereferenceSimple pointerDeref, StructDefinition structDefinition, ListIterator stmtIt, ControlFlowBlock currentBlock, Statement currentStmt) { - this.structDefinition = structDefinition; - this.currentBlock = currentBlock; - this.stmtIt = stmtIt; - this.pointerDeref = pointerDeref; - this.currentStmt = currentStmt; - } - - @Override - public List getMemberNames() { - Collection structMemberVars = structDefinition.getAllVariables(false); - ArrayList memberNames = new ArrayList<>(); - for(Variable structMemberVar : structMemberVars) { - memberNames.add(structMemberVar.getLocalName()); - } - return memberNames; - } - - @Override - public StructUnwinding.RValueUnwinding getMemberUnwinding(String memberName) { - return new StructUnwinding.RValueUnwinding() { - @Override - public SymbolType getType() { - return structDefinition.getMember(memberName).getType(); - } - - @Override - public ArraySpec getArraySpec() { - return structDefinition.getMember(memberName).getArraySpec(); - } - - @Override - public RValue getUnwinding(ProgramScope programScope) { - ConstantRef memberOffsetConstant = PassNStructPointerRewriting.getMemberOffsetConstant(programScope, structDefinition, memberName); - Variable member = structDefinition.getMember(memberName); - Scope scope = programScope.getScope(currentBlock.getScope()); - Variable memberAddress = scope.addVariableIntermediate(); - memberAddress.setType(new SymbolTypePointer(member.getType())); - CastValue structTypedPointer = new CastValue(new SymbolTypePointer(member.getType()), pointerDeref.getPointer()); - // Add statement $1 = ptr_struct + OFFSET_STRUCT_NAME_MEMBER - stmtIt.add(new StatementAssignment((LValue) memberAddress.getRef(), structTypedPointer, Operators.PLUS, memberOffsetConstant, true, currentStmt.getSource(), currentStmt.getComments())); - // Unwind to *(ptr_struct+OFFSET_STRUCT_NAME_MEMBER) - return new PointerDereferenceSimple(memberAddress.getRef()); - } - - @Override - public RValue getArrayUnwinding(ProgramScope scope, ConstantValue arraySize) { - throw new RuntimeException("TODO: Implement!"); - } - }; - } - - } - - /** Unwinding for a indexed pointer deref to a struct. */ - private static class StructMemberUnwindingPointerDerefIndexed implements StructUnwinding.StructMemberUnwinding { - private final PointerDereferenceIndexed pointerDeref; - private final StructDefinition structDefinition; - private final ControlFlowBlock currentBlock; - private final ListIterator stmtIt; - private final Statement currentStmt; - - StructMemberUnwindingPointerDerefIndexed(PointerDereferenceIndexed pointerDeref, StructDefinition structDefinition, ListIterator stmtIt, ControlFlowBlock currentBlock, Statement currentStmt) { - this.structDefinition = structDefinition; - this.currentBlock = currentBlock; - this.stmtIt = stmtIt; - this.pointerDeref = pointerDeref; - this.currentStmt = currentStmt; - } - - @Override - public List getMemberNames() { - Collection structMemberVars = structDefinition.getAllVariables(false); - ArrayList memberNames = new ArrayList<>(); - for(Variable structMemberVar : structMemberVars) { - memberNames.add(structMemberVar.getLocalName()); - } - return memberNames; - } - - @Override - public StructUnwinding.RValueUnwinding getMemberUnwinding(String memberName) { - return new StructUnwinding.RValueUnwinding() { - @Override - public SymbolType getType() { - return structDefinition.getMember(memberName).getType(); - } - - @Override - public ArraySpec getArraySpec() { - return structDefinition.getMember(memberName).getArraySpec(); - } - - @Override - public RValue getUnwinding(ProgramScope programScope) { - ConstantRef memberOffsetConstant = PassNStructPointerRewriting.getMemberOffsetConstant(programScope, structDefinition, memberName); - Variable member = structDefinition.getMember(memberName); - Scope scope = programScope.getScope(currentBlock.getScope()); - Variable memberAddress = scope.addVariableIntermediate(); - memberAddress.setType(new SymbolTypePointer(member.getType())); - CastValue structTypedPointer = new CastValue(new SymbolTypePointer(member.getType()), pointerDeref.getPointer()); - // Add statement $1 = ptr_struct + OFFSET_STRUCT_NAME_MEMBER - stmtIt.add(new StatementAssignment((LValue) memberAddress.getRef(), structTypedPointer, Operators.PLUS, memberOffsetConstant, true, currentStmt.getSource(), currentStmt.getComments())); - // Unwind to *(ptr_struct+OFFSET_STRUCT_NAME_MEMBER[idx] - return new PointerDereferenceIndexed(memberAddress.getRef(), pointerDeref.getIndex()); - } - - @Override - public RValue getArrayUnwinding(ProgramScope scope, ConstantValue arraySize) { - throw new RuntimeException("TODO: Implement!"); - } - }; - } - - } - - /** Unwinding for constant struct value. */ - private static class StructMemberUnwindingConstantValue implements StructUnwinding.StructMemberUnwinding { - private final ConstantStructValue constantStructValue; - private final StructDefinition structDefinition; - - StructMemberUnwindingConstantValue(ConstantStructValue constantStructValue, StructDefinition structDefinition) { - this.constantStructValue = constantStructValue; - this.structDefinition = structDefinition; - } - - @Override - public List getMemberNames() { - Collection structMemberVars = structDefinition.getAllVariables(false); - ArrayList memberNames = new ArrayList<>(); - for(Variable structMemberVar : structMemberVars) { - memberNames.add(structMemberVar.getLocalName()); - } - return memberNames; - } - - @Override - public StructUnwinding.RValueUnwinding getMemberUnwinding(String memberName) { - return new StructUnwinding.RValueUnwinding() { - @Override - public SymbolType getType() { - return structDefinition.getMember(memberName).getType(); - } - - @Override - public ArraySpec getArraySpec() { - return structDefinition.getMember(memberName).getArraySpec(); - } - - @Override - public RValue getUnwinding(ProgramScope programScope) { - Variable member = structDefinition.getMember(memberName); - return constantStructValue.getValue(member.getRef()); - } - - @Override - public RValue getArrayUnwinding(ProgramScope scope, ConstantValue arraySize) { - // Create a constant variable holding the array - String constName = scope.allocateIntermediateVariableName(); - Variable member = structDefinition.getMember(memberName); - SymbolType memberType = member.getType(); - ConstantValue constValue = constantStructValue.getValue(member.getRef()); - Variable constVar = Variable.createConstant(constName, memberType, scope, new ArraySpec(arraySize), constValue, Scope.SEGMENT_DATA_DEFAULT); - scope.add(constVar); - return new MemcpyValue(new PointerDereferenceSimple(constVar.getRef()), arraySize); - } - }; - } - - } - - /** Unwinding for a struct value with C-classic memory layout. */ - private static class StructVariableMemberUnwinding implements StructUnwinding.StructMemberUnwinding { - private Variable variable; - private StructDefinition structDefinition; - private final ControlFlowBlock currentBlock; - private final ListIterator stmtIt; - private final Statement currentStmt; - - public StructVariableMemberUnwinding(Variable variable, StructDefinition structDefinition, ControlFlowBlock currentBlock, ListIterator stmtIt, Statement currentStmt) { - this.variable = variable; - this.structDefinition = structDefinition; - this.currentBlock = currentBlock; - this.stmtIt = stmtIt; - this.currentStmt = currentStmt; - } - - @Override - public List getMemberNames() { - Collection structMemberVars = structDefinition.getAllVars(false); - ArrayList memberNames = new ArrayList<>(); - for(Variable structMemberVar : structMemberVars) { - memberNames.add(structMemberVar.getLocalName()); - } - return memberNames; - } - - @Override - public StructUnwinding.RValueUnwinding getMemberUnwinding(String memberName) { - return new StructUnwinding.RValueUnwinding() { - @Override - public SymbolType getType() { - return structDefinition.getMember(memberName).getType(); - } - - @Override - public ArraySpec getArraySpec() { - return structDefinition.getMember(memberName).getArraySpec(); - } - - @Override - public RValue getUnwinding(ProgramScope programScope) { - ConstantRef memberOffsetConstant = PassNStructPointerRewriting.getMemberOffsetConstant(programScope, structDefinition, memberName); - Variable member = structDefinition.getMember(memberName); - ConstantSymbolPointer structPointer = new ConstantSymbolPointer(variable.getRef()); - ConstantCastValue structTypedPointer; - if(member.isArray()) { - // Pointer to array element type - SymbolTypePointer arrayType = (SymbolTypePointer) member.getType(); - structTypedPointer = new ConstantCastValue(new SymbolTypePointer(arrayType.getElementType()), structPointer); - // Calculate member address (elementtype*)&struct + OFFSET_STRUCT_NAME_MEMBER - ConstantBinary memberArrayPointer = new ConstantBinary(structTypedPointer, Operators.PLUS, memberOffsetConstant); - // Unwind to *(&struct + OFFSET_STRUCT_NAME_MEMBER) - return memberArrayPointer; - } else { - // Pointer to member element type - structTypedPointer = new ConstantCastValue(new SymbolTypePointer(member.getType()), structPointer); - // Calculate member address (elementtype*)&struct + OFFSET_STRUCT_NAME_MEMBER - ConstantBinary memberArrayPointer = new ConstantBinary(structTypedPointer, Operators.PLUS, memberOffsetConstant); - // Unwind to *(&struct + OFFSET_STRUCT_NAME_MEMBER) - return new PointerDereferenceSimple(memberArrayPointer); - } - } - - @Override - public RValue getArrayUnwinding(ProgramScope scope, ConstantValue arraySize) { - RValue rValueMemberVarPointer = getUnwinding(scope); - LValue rValueMemberVarRef = new PointerDereferenceSimple(rValueMemberVarPointer); - return new MemcpyValue(rValueMemberVarRef, arraySize); - } - - }; - } - - } - - /** Unwinding for StructZero */ - private static class StructMemberUnwindingZero implements StructUnwinding.StructMemberUnwinding { - private StructZero structZero; - private StructDefinition structDefinition; - - public StructMemberUnwindingZero(StructZero structZero, StructDefinition structDefinition) { - this.structZero = structZero; - this.structDefinition = structDefinition; - } - - @Override - public List getMemberNames() { - Collection structMemberVars = structDefinition.getAllVariables(false); - ArrayList memberNames = new ArrayList<>(); - for(Variable structMemberVar : structMemberVars) { - memberNames.add(structMemberVar.getLocalName()); - } - return memberNames; - } - - @Override - public StructUnwinding.RValueUnwinding getMemberUnwinding(String memberName) { - return new StructUnwinding.RValueUnwinding() { - @Override - public SymbolType getType() { - return structDefinition.getMember(memberName).getType(); - } - - @Override - public ArraySpec getArraySpec() { - return structDefinition.getMember(memberName).getArraySpec(); - } - - @Override - public RValue getUnwinding(ProgramScope programScope) { - return ZeroConstantValues.zeroValue(getType(), programScope); - } - - @Override - public RValue getArrayUnwinding(ProgramScope scope, ConstantValue arraySize) { - return new MemsetValue(arraySize); - } - }; - } - - } - - /** Unwinding for ValueList. */ - private static class StructMemberUnwindingValueList implements StructUnwinding.StructMemberUnwinding { - - StructDefinition structDefinition; - ValueList valueList; - - public StructMemberUnwindingValueList(ValueList valueList, StructDefinition structDefinition) { - this.valueList = valueList; - this.structDefinition = structDefinition; - } - - @Override - public List getMemberNames() { - Collection structMemberVars = structDefinition.getAllVariables(false); - ArrayList memberNames = new ArrayList<>(); - for(Variable structMemberVar : structMemberVars) { - memberNames.add(structMemberVar.getLocalName()); - } - return memberNames; - } - - @Override - public StructUnwinding.RValueUnwinding getMemberUnwinding(String memberName) { - return new StructUnwinding.RValueUnwinding() { - @Override - public SymbolType getType() { - return structDefinition.getMember(memberName).getType(); - } - - @Override - public ArraySpec getArraySpec() { - return structDefinition.getMember(memberName).getArraySpec(); - } - - @Override - public RValue getUnwinding(ProgramScope programScope) { - int memberIndex = getMemberNames().indexOf(memberName); - return valueList.getList().get(memberIndex); - } - - @Override - public RValue getArrayUnwinding(ProgramScope scope, ConstantValue arraySize) { - throw new RuntimeException("TODO: Implement!"); - } - }; - } - - } } diff --git a/src/main/java/dk/camelot64/kickc/passes/unwinding/ConstantValueUnwinding.java b/src/main/java/dk/camelot64/kickc/passes/unwinding/ConstantValueUnwinding.java new file mode 100644 index 000000000..bf6e7b1ba --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/unwinding/ConstantValueUnwinding.java @@ -0,0 +1,55 @@ +package dk.camelot64.kickc.passes.unwinding; + +import dk.camelot64.kickc.model.symbols.ArraySpec; +import dk.camelot64.kickc.model.symbols.ProgramScope; +import dk.camelot64.kickc.model.symbols.Scope; +import dk.camelot64.kickc.model.symbols.Variable; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.values.*; + +/** Unwinding a constant value. */ +public class ConstantValueUnwinding implements RValueUnwinding { + private final SymbolType symbolType; + private final ArraySpec arraySpec; + private final ConstantValue value; + + public ConstantValueUnwinding(SymbolType symbolType, ArraySpec arraySpec, ConstantValue value) { + this.symbolType = symbolType; + this.arraySpec = arraySpec; + this.value = value; + } + + @Override + public SymbolType getSymbolType() { + return symbolType; + } + + @Override + public ArraySpec getArraySpec() { + return arraySpec; + } + + @Override + public RValue getUnwinding(ProgramScope programScope) { + return value; + } + + @Override + public boolean isBulkCopyable() { + return getArraySpec()!=null; + } + + @Override + public LValue getBulkLValue(ProgramScope scope) { + throw new InternalError("Not a valid LValue!"); + } + + @Override + public RValue getBulkRValue(ProgramScope scope) { + String constName = scope.allocateIntermediateVariableName(); + Variable constVar = Variable.createConstant(constName, symbolType, scope, getArraySpec(), value, Scope.SEGMENT_DATA_DEFAULT); + scope.add(constVar); + return new MemcpyValue(new PointerDereferenceSimple(constVar.getRef()), getArraySpec().getArraySize()); + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/unwinding/RValueUnwinding.java b/src/main/java/dk/camelot64/kickc/passes/unwinding/RValueUnwinding.java new file mode 100644 index 000000000..62dc51c7e --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/unwinding/RValueUnwinding.java @@ -0,0 +1,55 @@ +package dk.camelot64.kickc.passes.unwinding; + +import dk.camelot64.kickc.model.symbols.ArraySpec; +import dk.camelot64.kickc.model.symbols.ProgramScope; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.values.LValue; +import dk.camelot64.kickc.model.values.RValue; + +/** + * Assignment unwinding value used for copying one value to another using the most effective method. + * Assignment uses typed copy for simple types. For arrays/struct values bulk-copying of memory is used when feasible and member-by-member copying when necessary. + */ +public interface RValueUnwinding { + + /** + * Get the type of the value + * @return The type of the value + */ + SymbolType getSymbolType(); + + /** + * Get the array nature of the value + * @return The array nature of the value + */ + ArraySpec getArraySpec(); + + /** + * Get the RValue to use in the assignment as LValue - and as RValue if the member is a not an array value + * + * @param programScope The program scope + * @return The unwinding of the member + */ + RValue getUnwinding(ProgramScope programScope); + + /** + * Determine if the value can be bulk memory copied + * @return true if the value can be bulk memory copied (from or to). + */ + boolean isBulkCopyable(); + + /** + * Get Lvalue to use when for copying/setting a bulk value at once. Must returns a byte* type. + * @param scope The program scope + * @return The value to use as RValue + */ + LValue getBulkLValue(ProgramScope scope); + + /** + * Get Rvalue to use when for copying/setting a bulk value at once. Typically returns a memset/memcpy commands. + * @param scope The program scope + * @return The value to use as RValue + */ + RValue getBulkRValue(ProgramScope scope); + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/unwinding/SimpleRValueUnwinding.java b/src/main/java/dk/camelot64/kickc/passes/unwinding/SimpleRValueUnwinding.java new file mode 100644 index 000000000..511c459ef --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/unwinding/SimpleRValueUnwinding.java @@ -0,0 +1,51 @@ +package dk.camelot64.kickc.passes.unwinding; + +import dk.camelot64.kickc.model.symbols.ArraySpec; +import dk.camelot64.kickc.model.symbols.ProgramScope; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.values.LValue; +import dk.camelot64.kickc.model.values.RValue; + +/** Value unwinding that just returns a non-unwindable RValue. */ +class SimpleRValueUnwinding implements RValueUnwinding { + private final SymbolType type; + private final ArraySpec arraySpec; + private final RValue memberValue; + + public SimpleRValueUnwinding(SymbolType type, ArraySpec arraySpec, RValue memberValue) { + this.type = type; + this.arraySpec = arraySpec; + this.memberValue = memberValue; + } + + @Override + public SymbolType getSymbolType() { + return type; + } + + @Override + public ArraySpec getArraySpec() { + return arraySpec; + } + + @Override + public RValue getUnwinding(ProgramScope programScope) { + return memberValue; + } + + @Override + public boolean isBulkCopyable() { + return false; + } + + @Override + public LValue getBulkLValue(ProgramScope scope) { + throw new RuntimeException("TODO: Implement!"); + } + + @Override + public RValue getBulkRValue(ProgramScope scope) { + throw new RuntimeException("TODO: Implement!"); + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwinding.java b/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwinding.java new file mode 100644 index 000000000..d197f53fa --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwinding.java @@ -0,0 +1,23 @@ +package dk.camelot64.kickc.passes.unwinding; + +import java.util.List; + +/** Assignment unwinding information for a struct value into separate members. */ +public interface StructMemberUnwinding { + + /** + * Get the names of the members of the struct + * + * @return the names + */ + List getMemberNames(); + + /** + * Get the RValue unwinding to use for copying a single member of the struct + * + * @param memberName The member name + * @return The unwinding of the member + */ + RValueUnwinding getMemberUnwinding(String memberName); + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwindingConstantValue.java b/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwindingConstantValue.java new file mode 100644 index 000000000..1da772aec --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwindingConstantValue.java @@ -0,0 +1,43 @@ +package dk.camelot64.kickc.passes.unwinding; + +import dk.camelot64.kickc.model.symbols.ArraySpec; +import dk.camelot64.kickc.model.symbols.StructDefinition; +import dk.camelot64.kickc.model.symbols.Variable; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.values.ConstantStructValue; +import dk.camelot64.kickc.model.values.ConstantValue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** Member unwinding for constant struct value. */ +public class StructMemberUnwindingConstantValue implements StructMemberUnwinding { + private final ConstantStructValue constantStructValue; + private final StructDefinition structDefinition; + + public StructMemberUnwindingConstantValue(ConstantStructValue constantStructValue, StructDefinition structDefinition) { + this.constantStructValue = constantStructValue; + this.structDefinition = structDefinition; + } + + @Override + public List getMemberNames() { + Collection structMemberVars = structDefinition.getAllVariables(false); + ArrayList memberNames = new ArrayList<>(); + for(Variable structMemberVar : structMemberVars) { + memberNames.add(structMemberVar.getLocalName()); + } + return memberNames; + } + + @Override + public RValueUnwinding getMemberUnwinding(String memberName) { + final Variable member = structDefinition.getMember(memberName); + final SymbolType type = member.getType(); + final ArraySpec arraySpec = member.getArraySpec(); + final ConstantValue memberValue = constantStructValue.getValue(member.getRef()); + return new ConstantValueUnwinding(type, arraySpec, memberValue); + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwindingPointerDerefIndexed.java b/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwindingPointerDerefIndexed.java new file mode 100644 index 000000000..01b548db1 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwindingPointerDerefIndexed.java @@ -0,0 +1,88 @@ +package dk.camelot64.kickc.passes.unwinding; + +import dk.camelot64.kickc.model.ControlFlowBlock; +import dk.camelot64.kickc.model.operators.Operators; +import dk.camelot64.kickc.model.statements.Statement; +import dk.camelot64.kickc.model.statements.StatementAssignment; +import dk.camelot64.kickc.model.symbols.*; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.types.SymbolTypePointer; +import dk.camelot64.kickc.model.values.*; +import dk.camelot64.kickc.passes.PassNStructPointerRewriting; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.ListIterator; + +/** Unwinding for a indexed pointer deref to a struct. */ +public class StructMemberUnwindingPointerDerefIndexed implements StructMemberUnwinding { + private final PointerDereferenceIndexed pointerDeref; + private final StructDefinition structDefinition; + private final ControlFlowBlock currentBlock; + private final ListIterator stmtIt; + private final Statement currentStmt; + + public StructMemberUnwindingPointerDerefIndexed(PointerDereferenceIndexed pointerDeref, StructDefinition structDefinition, ListIterator stmtIt, ControlFlowBlock currentBlock, Statement currentStmt) { + this.structDefinition = structDefinition; + this.currentBlock = currentBlock; + this.stmtIt = stmtIt; + this.pointerDeref = pointerDeref; + this.currentStmt = currentStmt; + } + + @Override + public List getMemberNames() { + Collection structMemberVars = structDefinition.getAllVariables(false); + ArrayList memberNames = new ArrayList<>(); + for(Variable structMemberVar : structMemberVars) { + memberNames.add(structMemberVar.getLocalName()); + } + return memberNames; + } + + @Override + public RValueUnwinding getMemberUnwinding(String memberName) { + return new RValueUnwinding() { + @Override + public SymbolType getSymbolType() { + return structDefinition.getMember(memberName).getType(); + } + + @Override + public ArraySpec getArraySpec() { + return structDefinition.getMember(memberName).getArraySpec(); + } + + @Override + public RValue getUnwinding(ProgramScope programScope) { + ConstantRef memberOffsetConstant = PassNStructPointerRewriting.getMemberOffsetConstant(programScope, structDefinition, memberName); + Variable member = structDefinition.getMember(memberName); + Scope scope = programScope.getScope(currentBlock.getScope()); + Variable memberAddress = scope.addVariableIntermediate(); + memberAddress.setType(new SymbolTypePointer(member.getType())); + CastValue structTypedPointer = new CastValue(new SymbolTypePointer(member.getType()), pointerDeref.getPointer()); + // Add statement $1 = ptr_struct + OFFSET_STRUCT_NAME_MEMBER + stmtIt.add(new StatementAssignment((LValue) memberAddress.getRef(), structTypedPointer, Operators.PLUS, memberOffsetConstant, true, currentStmt.getSource(), currentStmt.getComments())); + // Unwind to *(ptr_struct+OFFSET_STRUCT_NAME_MEMBER[idx] + return new PointerDereferenceIndexed(memberAddress.getRef(), pointerDeref.getIndex()); + } + + @Override + public boolean isBulkCopyable() { + return getArraySpec()!=null; + } + + @Override + public LValue getBulkLValue(ProgramScope scope) { + return (LValue) getUnwinding(scope); + } + + @Override + public RValue getBulkRValue(ProgramScope scope) { + throw new RuntimeException("TODO: Implement!"); + } + }; + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwindingPointerDerefSimple.java b/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwindingPointerDerefSimple.java new file mode 100644 index 000000000..a2d4d4525 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwindingPointerDerefSimple.java @@ -0,0 +1,89 @@ +package dk.camelot64.kickc.passes.unwinding; + +import dk.camelot64.kickc.model.ControlFlowBlock; +import dk.camelot64.kickc.model.operators.Operators; +import dk.camelot64.kickc.model.statements.Statement; +import dk.camelot64.kickc.model.statements.StatementAssignment; +import dk.camelot64.kickc.model.symbols.*; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.types.SymbolTypePointer; +import dk.camelot64.kickc.model.values.*; +import dk.camelot64.kickc.passes.PassNStructPointerRewriting; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.ListIterator; + +/** Unwinding for a simple pointer deref to a struct. */ +public class StructMemberUnwindingPointerDerefSimple implements StructMemberUnwinding { + + private final StructDefinition structDefinition; + private final ControlFlowBlock currentBlock; + private final ListIterator stmtIt; + private final PointerDereferenceSimple pointerDeref; + private final Statement currentStmt; + + public StructMemberUnwindingPointerDerefSimple(PointerDereferenceSimple pointerDeref, StructDefinition structDefinition, ListIterator stmtIt, ControlFlowBlock currentBlock, Statement currentStmt) { + this.structDefinition = structDefinition; + this.currentBlock = currentBlock; + this.stmtIt = stmtIt; + this.pointerDeref = pointerDeref; + this.currentStmt = currentStmt; + } + + @Override + public List getMemberNames() { + Collection structMemberVars = structDefinition.getAllVariables(false); + ArrayList memberNames = new ArrayList<>(); + for(Variable structMemberVar : structMemberVars) { + memberNames.add(structMemberVar.getLocalName()); + } + return memberNames; + } + + @Override + public RValueUnwinding getMemberUnwinding(String memberName) { + return new RValueUnwinding() { + @Override + public SymbolType getSymbolType() { + return structDefinition.getMember(memberName).getType(); + } + + @Override + public ArraySpec getArraySpec() { + return structDefinition.getMember(memberName).getArraySpec(); + } + + @Override + public RValue getUnwinding(ProgramScope programScope) { + ConstantRef memberOffsetConstant = PassNStructPointerRewriting.getMemberOffsetConstant(programScope, structDefinition, memberName); + Variable member = structDefinition.getMember(memberName); + Scope scope = programScope.getScope(currentBlock.getScope()); + Variable memberAddress = scope.addVariableIntermediate(); + memberAddress.setType(new SymbolTypePointer(member.getType())); + CastValue structTypedPointer = new CastValue(new SymbolTypePointer(member.getType()), pointerDeref.getPointer()); + // Add statement $1 = ptr_struct + OFFSET_STRUCT_NAME_MEMBER + stmtIt.add(new StatementAssignment((LValue) memberAddress.getRef(), structTypedPointer, Operators.PLUS, memberOffsetConstant, true, currentStmt.getSource(), currentStmt.getComments())); + // Unwind to *(ptr_struct+OFFSET_STRUCT_NAME_MEMBER) + return new PointerDereferenceSimple(memberAddress.getRef()); + } + + @Override + public boolean isBulkCopyable() { + return getArraySpec()!=null; + } + + @Override + public LValue getBulkLValue(ProgramScope scope) { + return (LValue) getUnwinding(scope); + } + + @Override + public RValue getBulkRValue(ProgramScope scope) { + throw new RuntimeException("TODO: Implement!"); + } + }; + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwindingValueList.java b/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwindingValueList.java new file mode 100644 index 000000000..46de9fe3f --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwindingValueList.java @@ -0,0 +1,44 @@ +package dk.camelot64.kickc.passes.unwinding; + +import dk.camelot64.kickc.model.symbols.ArraySpec; +import dk.camelot64.kickc.model.symbols.StructDefinition; +import dk.camelot64.kickc.model.symbols.Variable; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.values.RValue; +import dk.camelot64.kickc.model.values.ValueList; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** Member unwinding for a struct valueList. */ +public class StructMemberUnwindingValueList implements StructMemberUnwinding { + + private final StructDefinition structDefinition; + private final ValueList valueList; + + public StructMemberUnwindingValueList(ValueList valueList, StructDefinition structDefinition) { + this.valueList = valueList; + this.structDefinition = structDefinition; + } + + @Override + public List getMemberNames() { + Collection structMemberVars = structDefinition.getAllVariables(false); + ArrayList memberNames = new ArrayList<>(); + for(Variable structMemberVar : structMemberVars) { + memberNames.add(structMemberVar.getLocalName()); + } + return memberNames; + } + + @Override + public RValueUnwinding getMemberUnwinding(String memberName) { + final SymbolType type = structDefinition.getMember(memberName).getType(); + final ArraySpec arraySpec = structDefinition.getMember(memberName).getArraySpec(); + int memberIndex = getMemberNames().indexOf(memberName); + final RValue memberValue = valueList.getList().get(memberIndex); + return new SimpleRValueUnwinding(type, arraySpec, memberValue); + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwindingZero.java b/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwindingZero.java new file mode 100644 index 000000000..54cf3a9b6 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/unwinding/StructMemberUnwindingZero.java @@ -0,0 +1,37 @@ +package dk.camelot64.kickc.passes.unwinding; + +import dk.camelot64.kickc.model.symbols.StructDefinition; +import dk.camelot64.kickc.model.symbols.Variable; +import dk.camelot64.kickc.model.values.StructZero; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** Unwinding for StructZero */ +public class StructMemberUnwindingZero implements StructMemberUnwinding { + private StructZero structZero; + private StructDefinition structDefinition; + + public StructMemberUnwindingZero(StructZero structZero, StructDefinition structDefinition) { + this.structZero = structZero; + this.structDefinition = structDefinition; + } + + @Override + public List getMemberNames() { + Collection structMemberVars = structDefinition.getAllVariables(false); + ArrayList memberNames = new ArrayList<>(); + for(Variable structMemberVar : structMemberVars) { + memberNames.add(structMemberVar.getLocalName()); + } + return memberNames; + } + + @Override + public RValueUnwinding getMemberUnwinding(String memberName) { + Variable member = structDefinition.getMember(memberName); + return new ZeroValueUnwinding(member.getType(), member.getArraySpec()); + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/unwinding/StructVariableMemberUnwinding.java b/src/main/java/dk/camelot64/kickc/passes/unwinding/StructVariableMemberUnwinding.java new file mode 100644 index 000000000..0fa8ead4c --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/unwinding/StructVariableMemberUnwinding.java @@ -0,0 +1,141 @@ +package dk.camelot64.kickc.passes.unwinding; + +import dk.camelot64.kickc.model.ControlFlowBlock; +import dk.camelot64.kickc.model.operators.Operators; +import dk.camelot64.kickc.model.statements.Statement; +import dk.camelot64.kickc.model.symbols.ArraySpec; +import dk.camelot64.kickc.model.symbols.ProgramScope; +import dk.camelot64.kickc.model.symbols.StructDefinition; +import dk.camelot64.kickc.model.symbols.Variable; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.types.SymbolTypePointer; +import dk.camelot64.kickc.model.values.*; +import dk.camelot64.kickc.passes.PassNStructPointerRewriting; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.ListIterator; + +/** Unwinding for a struct value with C-classic memory layout. */ +public class StructVariableMemberUnwinding implements StructMemberUnwinding { + private Variable variable; + private StructDefinition structDefinition; + private final ControlFlowBlock currentBlock; + private final ListIterator stmtIt; + private final Statement currentStmt; + + public StructVariableMemberUnwinding(Variable variable, StructDefinition structDefinition, ControlFlowBlock currentBlock, ListIterator stmtIt, Statement currentStmt) { + this.variable = variable; + this.structDefinition = structDefinition; + this.currentBlock = currentBlock; + this.stmtIt = stmtIt; + this.currentStmt = currentStmt; + } + + @Override + public List getMemberNames() { + Collection structMemberVars = structDefinition.getAllVars(false); + ArrayList memberNames = new ArrayList<>(); + for(Variable structMemberVar : structMemberVars) { + memberNames.add(structMemberVar.getLocalName()); + } + return memberNames; + } + + @Override + public RValueUnwinding getMemberUnwinding(String memberName) { + final SymbolType symbolType = structDefinition.getMember(memberName).getType(); + final ArraySpec arraySpec = structDefinition.getMember(memberName).getArraySpec(); + + if(arraySpec==null) { + // Simple member value - unwind to value of member *((type*)&struct + OFFSET_MEMBER) + return new RValueUnwinding() { + @Override + public SymbolType getSymbolType() { + return symbolType; + } + + @Override + public ArraySpec getArraySpec() { + return arraySpec; + } + + @Override + public RValue getUnwinding(ProgramScope programScope) { + ConstantRef memberOffsetConstant = PassNStructPointerRewriting.getMemberOffsetConstant(programScope, structDefinition, memberName); + ConstantSymbolPointer structPointer = new ConstantSymbolPointer(variable.getRef()); + // Pointer to member type + ConstantCastValue structTypedPointer = new ConstantCastValue(new SymbolTypePointer(getSymbolType()), structPointer); + // Calculate member address (type*)&struct + OFFSET_MEMBER + ConstantBinary memberArrayPointer = new ConstantBinary(structTypedPointer, Operators.PLUS, memberOffsetConstant); + // Unwind to *((type*)&struct + OFFSET_MEMBER) + return new PointerDereferenceSimple(memberArrayPointer); + } + + @Override + public boolean isBulkCopyable() { + return false; + } + + @Override + public LValue getBulkLValue(ProgramScope scope) { + return null; + } + + @Override + public RValue getBulkRValue(ProgramScope scope) { + return null; + } + }; + } else { + // Array struct member - unwind to pointer to first element (elmtype*)&struct + OFFSET_MEMBER + return new RValueUnwinding() { + @Override + public SymbolType getSymbolType() { + return symbolType; + } + + @Override + public ArraySpec getArraySpec() { + return arraySpec; + } + + @Override + public RValue getUnwinding(ProgramScope programScope) { + ConstantRef memberOffsetConstant = PassNStructPointerRewriting.getMemberOffsetConstant(programScope, structDefinition, memberName); + ConstantSymbolPointer structPointer = new ConstantSymbolPointer(variable.getRef()); + // Pointer to member element type (elmtype*)&struct + SymbolTypePointer memberType = (SymbolTypePointer) getSymbolType(); + ConstantCastValue structTypedPointer = new ConstantCastValue(new SymbolTypePointer(memberType.getElementType()), structPointer); + // Calculate member address (elmtype*)&struct + OFFSET_MEMBER + ConstantBinary memberArrayPointer = new ConstantBinary(structTypedPointer, Operators.PLUS, memberOffsetConstant); + // Unwind to (elmtype*)&struct + OFFSET_MEMBER + return memberArrayPointer; + } + + @Override + public boolean isBulkCopyable() { + return true; + } + + @Override + public LValue getBulkLValue(ProgramScope scope) { + RValue memberArrayPointer = getUnwinding(scope); + return new PointerDereferenceSimple(memberArrayPointer); + + } + + @Override + public RValue getBulkRValue(ProgramScope scope) { + RValue memberArrayPointer = getUnwinding(scope); + return new MemcpyValue(new PointerDereferenceSimple(memberArrayPointer), getArraySpec().getArraySize()); + } + }; + + } + + + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/unwinding/StructVariableValueUnwinding.java b/src/main/java/dk/camelot64/kickc/passes/unwinding/StructVariableValueUnwinding.java new file mode 100644 index 000000000..c50edd95e --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/unwinding/StructVariableValueUnwinding.java @@ -0,0 +1,53 @@ +package dk.camelot64.kickc.passes.unwinding; + +import dk.camelot64.kickc.model.symbols.ArraySpec; +import dk.camelot64.kickc.model.symbols.ProgramScope; +import dk.camelot64.kickc.model.symbols.Variable; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.values.*; + +/** Value unwinding for a variable. */ +public class StructVariableValueUnwinding implements RValueUnwinding { + + private final Variable variable; + + public StructVariableValueUnwinding(Variable variable) { + this.variable = variable; + } + + @Override + public SymbolType getSymbolType() { + return variable.getType(); + } + + @Override + public ArraySpec getArraySpec() { + return variable.getArraySpec(); + } + + @Override + public RValue getUnwinding(ProgramScope programScope) { + ConstantSymbolPointer pointer = new ConstantSymbolPointer(variable.getRef()); + return pointer; + } + + @Override + public boolean isBulkCopyable() { + return getArraySpec()!=null; + } + + @Override + public LValue getBulkLValue(ProgramScope scope) { + ConstantSymbolPointer pointer = new ConstantSymbolPointer(variable.getRef()); + LValue pointerDeref = new PointerDereferenceSimple(pointer); + return pointerDeref; + } + + @Override + public RValue getBulkRValue(ProgramScope scope) { + ConstantSymbolPointer pointer = new ConstantSymbolPointer(variable.getRef()); + LValue pointerDeref = new PointerDereferenceSimple(pointer); + return new MemcpyValue(pointerDeref, getArraySpec().getArraySize()); + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/unwinding/ZeroValueUnwinding.java b/src/main/java/dk/camelot64/kickc/passes/unwinding/ZeroValueUnwinding.java new file mode 100644 index 000000000..489a7d4e0 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/unwinding/ZeroValueUnwinding.java @@ -0,0 +1,59 @@ +package dk.camelot64.kickc.passes.unwinding; + +import dk.camelot64.kickc.model.InternalError; +import dk.camelot64.kickc.model.operators.OperatorSizeOf; +import dk.camelot64.kickc.model.symbols.ArraySpec; +import dk.camelot64.kickc.model.symbols.ProgramScope; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.types.SymbolTypeStruct; +import dk.camelot64.kickc.model.values.*; + +/** Assignment unwinding for a zero value. */ +public class ZeroValueUnwinding implements RValueUnwinding { + private final SymbolType symbolType; + private final ArraySpec arraySpec; + + public ZeroValueUnwinding(SymbolType symbolType, ArraySpec arraySpec) { + this.symbolType = symbolType; + this.arraySpec = arraySpec; + } + + @Override + public SymbolType getSymbolType() { + return symbolType; + } + + @Override + public ArraySpec getArraySpec() { + return arraySpec; + } + + @Override + public RValue getUnwinding(ProgramScope programScope) { + return ZeroConstantValues.zeroValue(getSymbolType(), programScope); + } + + @Override + public boolean isBulkCopyable() { + return arraySpec!=null; + } + + @Override + public LValue getBulkLValue(ProgramScope scope) { + throw new InternalError("Not a legal LValue!"); + } + + @Override + public RValue getBulkRValue(ProgramScope scope) { + ConstantValue byteSize; + if(getArraySpec()!=null) { + byteSize = getArraySpec().getArraySize(); + } else if(symbolType instanceof SymbolTypeStruct) { + byteSize = OperatorSizeOf.getSizeOfConstantVar(scope, symbolType); + } else { + throw new InternalError("Bulk unwinding of zero value not handled "+symbolType.getTypeName()); + } + return new MemsetValue(byteSize); + } + +} diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index a73aa4609..da34d2f83 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -1127,14 +1127,24 @@ public class TestPrograms { assertError("struct-err-0", "Unknown struct type"); } + @Test + public void testStruct31() throws IOException, URISyntaxException { + compileAndCompare("struct-31"); + } + + @Test + public void testStruct30() throws IOException, URISyntaxException { + compileAndCompare("struct-30"); + } + @Test public void testStruct29() throws IOException, URISyntaxException { - compileAndCompare("struct-29", log()); + compileAndCompare("struct-29"); } @Test public void testStruct28() throws IOException, URISyntaxException { - compileAndCompare("struct-28", log()); + compileAndCompare("struct-28"); } @Test @@ -1189,7 +1199,7 @@ public class TestPrograms { @Test public void testStruct17() throws IOException, URISyntaxException { - compileAndCompare("struct-17"); + compileAndCompare("struct-17", log()); } @Test diff --git a/src/test/kc/struct-30.kc b/src/test/kc/struct-30.kc new file mode 100644 index 000000000..09dbbc1f0 --- /dev/null +++ b/src/test/kc/struct-30.kc @@ -0,0 +1,16 @@ +// Minimal struct with MemberUnwind behavior - array member and local initializer + +struct Point { + char x; + char[3] initials; +}; + +const char* SCREEN = 0x0400; + + +void main() { + struct Point point1 = { 2, "jg" }; + SCREEN[0] = point1.x; + SCREEN[1] = point1.initials[0]; + SCREEN[2] = point1.initials[1]; +} \ No newline at end of file diff --git a/src/test/kc/struct-31.kc b/src/test/kc/struct-31.kc new file mode 100644 index 000000000..753b78a70 --- /dev/null +++ b/src/test/kc/struct-31.kc @@ -0,0 +1,15 @@ +// Minimal struct with MemberUnwind behavior - simple members and local initializer + +struct Point { + char x; + char y; +}; + +const char* SCREEN = 0x0400; + + +void main() { + struct Point point1 = { 2, 3 }; + SCREEN[0] = point1.x; + SCREEN[1] = point1.y; +} \ No newline at end of file diff --git a/src/test/ref/declared-memory-var-4.log b/src/test/ref/declared-memory-var-4.log index 7a8824ac7..5b4422488 100644 --- a/src/test/ref/declared-memory-var-4.log +++ b/src/test/ref/declared-memory-var-4.log @@ -18,7 +18,7 @@ CONTROL FLOW GRAPH SSA *((byte*)&(struct foo) bar+(const byte) OFFSET_STRUCT_FOO_THING1) ← (byte) 'a' *((byte*)&(struct foo) bar+(const byte) OFFSET_STRUCT_FOO_THING2) ← (byte) 'b' *((byte*)&(struct foo) bar+(const byte) OFFSET_STRUCT_FOO_THING3) ← memcpy(*((const byte*) $0), (number) $c) - (struct foo) bar ← struct-unwound {*((byte*)&(struct foo) bar+(const byte) OFFSET_STRUCT_FOO_THING1), *((byte*)&(struct foo) bar+(const byte) OFFSET_STRUCT_FOO_THING2), (byte*)&(struct foo) bar+(const byte) OFFSET_STRUCT_FOO_THING3} + (struct foo) bar ← struct-unwound {*((byte*)&(struct foo) bar+(const byte) OFFSET_STRUCT_FOO_THING1), *((byte*)&(struct foo) bar+(const byte) OFFSET_STRUCT_FOO_THING2), *((byte*)&(struct foo) bar+(const byte) OFFSET_STRUCT_FOO_THING3)} to:@1 (void()) main() @@ -89,7 +89,7 @@ Simplifying constant pointer cast (byte*) 1024 Successful SSA optimization PassNCastSimplification Simple Condition (bool~) main::$0 [18] if((byte) main::j#1!=rangelast(0,$b)) goto main::@1 Successful SSA optimization Pass2ConditionalJumpSimplification -Removing C-classic struct-unwound assignment [3] (struct foo) bar ← struct-unwound {*((byte*)&(struct foo) bar+(const byte) OFFSET_STRUCT_FOO_THING1), *((byte*)&(struct foo) bar+(const byte) OFFSET_STRUCT_FOO_THING2), (byte*)&(struct foo) bar+(const byte) OFFSET_STRUCT_FOO_THING3} +Removing C-classic struct-unwound assignment [3] (struct foo) bar ← struct-unwound {*((byte*)&(struct foo) bar+(const byte) OFFSET_STRUCT_FOO_THING1), *((byte*)&(struct foo) bar+(const byte) OFFSET_STRUCT_FOO_THING2), *((byte*)&(struct foo) bar+(const byte) OFFSET_STRUCT_FOO_THING3)} Constant right-side identified [5] (byte*~) main::$1 ← (byte*)(const struct foo*) main::barp + (const byte) OFFSET_STRUCT_FOO_THING1 Constant right-side identified [8] (byte*~) main::$2 ← (byte*)(const struct foo*) main::barp + (const byte) OFFSET_STRUCT_FOO_THING2 Constant right-side identified [13] (byte*~) main::$3 ← (byte*)(const struct foo*) main::barp + (const byte) OFFSET_STRUCT_FOO_THING3 diff --git a/src/test/ref/struct-24.log b/src/test/ref/struct-24.log index 5c95309bc..959c132ce 100644 --- a/src/test/ref/struct-24.log +++ b/src/test/ref/struct-24.log @@ -19,7 +19,7 @@ CONTROL FLOW GRAPH SSA main: scope:[main] from @1 *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X) ← (byte) 0 *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS) ← memset((number) 2) - (struct Point) main::point1 ← struct-unwound {*((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X), (byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS} + (struct Point) main::point1 ← struct-unwound {*((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X), *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS)} *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X) ← (number) 2 *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + (number) 0) ← (byte) 'j' *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + (number) 1) ← (byte) 'g' @@ -81,7 +81,7 @@ Finalized unsigned number type (byte) 1 Finalized unsigned number type (byte) 1 Finalized unsigned number type (byte) 2 Successful SSA optimization PassNFinalizeNumberTypeConversions -Removing C-classic struct-unwound assignment [2] (struct Point) main::point1 ← struct-unwound {*((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X), (byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS} +Removing C-classic struct-unwound assignment [2] (struct Point) main::point1 ← struct-unwound {*((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X), *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS)} Simplifying expression containing zero (byte*)&main::point1 in [0] *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X) ← (byte) 0 Simplifying expression containing zero (byte*)&main::point1 in [3] *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X) ← (byte) 2 Simplifying expression containing zero (byte*)&main::point1+OFFSET_STRUCT_POINT_INITIALS in [4] *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + (byte) 0) ← (byte) 'j' diff --git a/src/test/ref/struct-26.log b/src/test/ref/struct-26.log index 275a08582..5f7cd8a3d 100644 --- a/src/test/ref/struct-26.log +++ b/src/test/ref/struct-26.log @@ -23,13 +23,13 @@ CONTROL FLOW GRAPH SSA main: scope:[main] from @1 *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X) ← (byte) 0 *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS) ← memset((number) 2) - (struct Point) main::point1 ← struct-unwound {*((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X), (byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS} + (struct Point) main::point1 ← struct-unwound {*((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X), *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS)} *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X) ← (number) 2 *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + (number) 0) ← (byte) 'j' *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + (number) 1) ← (byte) 'g' *((byte*)&(struct Point) main::point2+(const byte) OFFSET_STRUCT_POINT_X) ← *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X) *((byte*)&(struct Point) main::point2+(const byte) OFFSET_STRUCT_POINT_INITIALS) ← memcpy(*((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS), (number) 2) - (struct Point) main::point2 ← struct-unwound {*((byte*)&(struct Point) main::point2+(const byte) OFFSET_STRUCT_POINT_X), (byte*)&(struct Point) main::point2+(const byte) OFFSET_STRUCT_POINT_INITIALS} + (struct Point) main::point2 ← struct-unwound {*((byte*)&(struct Point) main::point2+(const byte) OFFSET_STRUCT_POINT_X), *((byte*)&(struct Point) main::point2+(const byte) OFFSET_STRUCT_POINT_INITIALS)} *((const byte*) SCREEN + (number) 0) ← *((byte*)&(struct Point) main::point2+(const byte) OFFSET_STRUCT_POINT_X) *((const byte*) SCREEN + (number) 1) ← *((byte*)&(struct Point) main::point2+(const byte) OFFSET_STRUCT_POINT_INITIALS + (number) 0) *((const byte*) SCREEN + (number) 2) ← *((byte*)&(struct Point) main::point2+(const byte) OFFSET_STRUCT_POINT_INITIALS + (number) 1) @@ -89,8 +89,8 @@ Finalized unsigned number type (byte) 1 Finalized unsigned number type (byte) 1 Finalized unsigned number type (byte) 2 Successful SSA optimization PassNFinalizeNumberTypeConversions -Removing C-classic struct-unwound assignment [2] (struct Point) main::point1 ← struct-unwound {*((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X), (byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS} -Removing C-classic struct-unwound assignment [8] (struct Point) main::point2 ← struct-unwound {*((byte*)&(struct Point) main::point2+(const byte) OFFSET_STRUCT_POINT_X), (byte*)&(struct Point) main::point2+(const byte) OFFSET_STRUCT_POINT_INITIALS} +Removing C-classic struct-unwound assignment [2] (struct Point) main::point1 ← struct-unwound {*((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X), *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS)} +Removing C-classic struct-unwound assignment [8] (struct Point) main::point2 ← struct-unwound {*((byte*)&(struct Point) main::point2+(const byte) OFFSET_STRUCT_POINT_X), *((byte*)&(struct Point) main::point2+(const byte) OFFSET_STRUCT_POINT_INITIALS)} Simplifying expression containing zero (byte*)&main::point1 in [0] *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X) ← (byte) 0 Simplifying expression containing zero (byte*)&main::point1 in [3] *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X) ← (byte) 2 Simplifying expression containing zero (byte*)&main::point1+OFFSET_STRUCT_POINT_INITIALS in [4] *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + (byte) 0) ← (byte) 'j' diff --git a/src/test/ref/struct-27.log b/src/test/ref/struct-27.log index 6385aec2f..1f970f070 100644 --- a/src/test/ref/struct-27.log +++ b/src/test/ref/struct-27.log @@ -16,7 +16,7 @@ CONTROL FLOW GRAPH SSA main: scope:[main] from @1 *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X) ← (byte) 2 *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS) ← memcpy(*((const byte*) $0), (number) 3) - (struct Point) main::point1 ← struct-unwound {*((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X), (byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS} + (struct Point) main::point1 ← struct-unwound {*((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X), *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS)} *((const byte*) SCREEN + (number) 0) ← *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X) *((const byte*) SCREEN + (number) 1) ← *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + (number) 0) *((const byte*) SCREEN + (number) 2) ← *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + (number) 1) @@ -65,7 +65,7 @@ Finalized unsigned number type (byte) 1 Finalized unsigned number type (byte) 1 Finalized unsigned number type (byte) 2 Successful SSA optimization PassNFinalizeNumberTypeConversions -Removing C-classic struct-unwound assignment [2] (struct Point) main::point1 ← struct-unwound {*((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X), (byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS} +Removing C-classic struct-unwound assignment [2] (struct Point) main::point1 ← struct-unwound {*((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X), *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS)} Simplifying expression containing zero (byte*)&main::point1 in [0] *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X) ← (byte) 2 Simplifying expression containing zero (byte*)&main::point1 in [3] *((const byte*) SCREEN + (byte) 0) ← *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X) Simplifying expression containing zero SCREEN in [3] *((const byte*) SCREEN + (byte) 0) ← *((byte*)&(struct Point) main::point1) diff --git a/src/test/ref/struct-28.log b/src/test/ref/struct-28.log index b88eb47bb..004a08cc9 100644 --- a/src/test/ref/struct-28.log +++ b/src/test/ref/struct-28.log @@ -16,7 +16,7 @@ CONTROL FLOW GRAPH SSA main: scope:[main] from @1 *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X) ← (byte) 2 *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS) ← memcpy(*((const byte*) $0), (number) 2) - (struct Point) main::point1 ← struct-unwound {*((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X), (byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS} + (struct Point) main::point1 ← struct-unwound {*((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X), *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS)} *((const byte*) SCREEN + (number) 0) ← *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X) *((const byte*) SCREEN + (number) 1) ← *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + (number) 0) *((const byte*) SCREEN + (number) 2) ← *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + (number) 1) @@ -65,7 +65,7 @@ Finalized unsigned number type (byte) 1 Finalized unsigned number type (byte) 1 Finalized unsigned number type (byte) 2 Successful SSA optimization PassNFinalizeNumberTypeConversions -Removing C-classic struct-unwound assignment [2] (struct Point) main::point1 ← struct-unwound {*((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X), (byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS} +Removing C-classic struct-unwound assignment [2] (struct Point) main::point1 ← struct-unwound {*((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X), *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_INITIALS)} Simplifying expression containing zero (byte*)&main::point1 in [0] *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X) ← (byte) 2 Simplifying expression containing zero (byte*)&main::point1 in [3] *((const byte*) SCREEN + (byte) 0) ← *((byte*)&(struct Point) main::point1+(const byte) OFFSET_STRUCT_POINT_X) Simplifying expression containing zero SCREEN in [3] *((const byte*) SCREEN + (byte) 0) ← *((byte*)&(struct Point) main::point1) diff --git a/src/test/ref/struct-29.log b/src/test/ref/struct-29.log index 1317b4b4e..2a15b83e4 100644 --- a/src/test/ref/struct-29.log +++ b/src/test/ref/struct-29.log @@ -12,7 +12,7 @@ CONTROL FLOW GRAPH SSA @begin: scope:[] from *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_X) ← (byte) 2 *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS) ← memcpy(*((const byte*) $0), (number) 2) - (struct Point) point1 ← struct-unwound {*((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_X), (byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS} + (struct Point) point1 ← struct-unwound {*((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_X), *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS)} to:@1 (void()) main() @@ -65,7 +65,7 @@ Finalized unsigned number type (byte) 1 Finalized unsigned number type (byte) 1 Finalized unsigned number type (byte) 2 Successful SSA optimization PassNFinalizeNumberTypeConversions -Removing C-classic struct-unwound assignment [2] (struct Point) point1 ← struct-unwound {*((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_X), (byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS} +Removing C-classic struct-unwound assignment [2] (struct Point) point1 ← struct-unwound {*((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_X), *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS)} Simplifying expression containing zero (byte*)&point1 in [0] *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_X) ← (byte) 2 Simplifying expression containing zero (byte*)&point1 in [3] *((const byte*) SCREEN + (byte) 0) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_X) Simplifying expression containing zero SCREEN in [3] *((const byte*) SCREEN + (byte) 0) ← *((byte*)&(struct Point) point1) diff --git a/src/test/ref/struct-30.asm b/src/test/ref/struct-30.asm new file mode 100644 index 000000000..6ccae1877 --- /dev/null +++ b/src/test/ref/struct-30.asm @@ -0,0 +1,24 @@ +// Minimal struct with MemberUnwind behavior - array member and local initializer +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 +main: { + .const point1_x = 2 + ldy #3 + !: + lda __0-1,y + sta point1_initials-1,y + dey + bne !- + lda #point1_x + sta SCREEN + lda point1_initials + sta SCREEN+1 + lda point1_initials+1 + sta SCREEN+2 + rts + point1_initials: .fill 3, 0 +} + __0: .text "jg" + .byte 0 diff --git a/src/test/ref/struct-30.cfg b/src/test/ref/struct-30.cfg new file mode 100644 index 000000000..0c8f2908b --- /dev/null +++ b/src/test/ref/struct-30.cfg @@ -0,0 +1,20 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() + +(void()) main() +main: scope:[main] from @1 + [4] *((const byte*) main::point1_initials) ← memcpy(*((const byte*) $0), (number) 3) + [5] *((const byte*) SCREEN) ← (const byte) main::point1_x + [6] *((const byte*) SCREEN+(byte) 1) ← *((const byte*) main::point1_initials) + [7] *((const byte*) SCREEN+(byte) 2) ← *((const byte*) main::point1_initials+(byte) 1) + to:main::@return +main::@return: scope:[main] from main + [8] return + to:@return diff --git a/src/test/ref/struct-30.log b/src/test/ref/struct-30.log new file mode 100644 index 000000000..a9f72f2ac --- /dev/null +++ b/src/test/ref/struct-30.log @@ -0,0 +1,324 @@ +Fixing struct type size struct Point to 4 +Fixing struct type SIZE_OF struct Point to 4 +Fixing struct type SIZE_OF struct Point to 4 +Created struct value member variable (byte) main::point1_x +Created struct value member variable (const byte*) main::point1_initials +Converted struct value to member variables (struct Point) main::point1 +Adding struct value member variable copy (byte) main::point1_x ← (byte) 2 +Adding struct value member variable copy *((const byte*) main::point1_initials) ← memcpy(*((const byte*) $0), (number) 3) +Replacing struct member reference (struct Point) main::point1.x with member unwinding reference (byte) main::point1_x +Replacing struct member reference (struct Point) main::point1.initials with member unwinding reference (const byte*) main::point1_initials +Replacing struct member reference (struct Point) main::point1.initials with member unwinding reference (const byte*) main::point1_initials +Identified constant variable (byte) main::point1_x + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@1 + +(void()) main() +main: scope:[main] from @1 + *((const byte*) main::point1_initials) ← memcpy(*((const byte*) $0), (number) 3) + *((const byte*) SCREEN + (number) 0) ← (const byte) main::point1_x + *((const byte*) SCREEN + (number) 1) ← *((const byte*) main::point1_initials + (number) 0) + *((const byte*) SCREEN + (number) 2) ← *((const byte*) main::point1_initials + (number) 1) + to:main::@return +main::@return: scope:[main] from main + return + to:@return +@1: scope:[] from @begin + call main + to:@2 +@2: scope:[] from @1 + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(const byte*) $0[(number) 3] = (string) "jg" +(label) @1 +(label) @2 +(label) @begin +(label) @end +(const byte*) Point::initials[(number) 3] = { fill( 3, 0) } +(byte) Point::x +(const byte*) SCREEN = (byte*)(number) $400 +(void()) main() +(label) main::@return +(const byte*) main::point1_initials[(number) 3] = { fill( 3, 0) } +(const byte) main::point1_x = (byte) 2 + +Adding number conversion cast (unumber) 0 in *((const byte*) SCREEN + (number) 0) ← (const byte) main::point1_x +Adding number conversion cast (unumber) 0 in *((const byte*) SCREEN + (number) 1) ← *((const byte*) main::point1_initials + (number) 0) +Adding number conversion cast (unumber) 1 in *((const byte*) SCREEN + (number) 1) ← *((const byte*) main::point1_initials + (unumber)(number) 0) +Adding number conversion cast (unumber) 1 in *((const byte*) SCREEN + (number) 2) ← *((const byte*) main::point1_initials + (number) 1) +Adding number conversion cast (unumber) 2 in *((const byte*) SCREEN + (number) 2) ← *((const byte*) main::point1_initials + (unumber)(number) 1) +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Simplifying constant integer cast 1 +Simplifying constant integer cast 1 +Simplifying constant integer cast 2 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 1 +Finalized unsigned number type (byte) 1 +Finalized unsigned number type (byte) 2 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simplifying expression containing zero SCREEN in [1] *((const byte*) SCREEN + (byte) 0) ← (const byte) main::point1_x +Simplifying expression containing zero main::point1_initials in [2] *((const byte*) SCREEN + (byte) 1) ← *((const byte*) main::point1_initials + (byte) 0) +Successful SSA optimization PassNSimplifyExpressionWithZero +Consolidated array index constant in *(SCREEN+1) +Consolidated array index constant in *(main::point1_initials+1) +Consolidated array index constant in *(SCREEN+2) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +CALL GRAPH +Calls in [] to main:2 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Culled Empty Block (label) @2 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() + +(void()) main() +main: scope:[main] from @1 + [4] *((const byte*) main::point1_initials) ← memcpy(*((const byte*) $0), (number) 3) + [5] *((const byte*) SCREEN) ← (const byte) main::point1_x + [6] *((const byte*) SCREEN+(byte) 1) ← *((const byte*) main::point1_initials) + [7] *((const byte*) SCREEN+(byte) 2) ← *((const byte*) main::point1_initials+(byte) 1) + to:main::@return +main::@return: scope:[main] from main + [8] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte) Point::x +(void()) main() + +Initial phi equivalence classes +Complete equivalence classes + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Minimal struct with MemberUnwind behavior - array member and local initializer + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin +__bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +__b1_from___bbegin: + jmp __b1 + // @1 +__b1: + // [2] call main + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: + // main +main: { + .const point1_x = 2 + // [4] *((const byte*) main::point1_initials) ← memcpy(*((const byte*) $0), (number) 3) -- _deref_pbuc1=_deref_pbuc2_memcpy_vbuc3 + ldy #3 + !: + lda __0-1,y + sta point1_initials-1,y + dey + bne !- + // [5] *((const byte*) SCREEN) ← (const byte) main::point1_x -- _deref_pbuc1=vbuc2 + lda #point1_x + sta SCREEN + // [6] *((const byte*) SCREEN+(byte) 1) ← *((const byte*) main::point1_initials) -- _deref_pbuc1=_deref_pbuc2 + lda point1_initials + sta SCREEN+1 + // [7] *((const byte*) SCREEN+(byte) 2) ← *((const byte*) main::point1_initials+(byte) 1) -- _deref_pbuc1=_deref_pbuc2 + lda point1_initials+1 + sta SCREEN+2 + jmp __breturn + // main::@return + __breturn: + // [8] return + rts + point1_initials: .fill 3, 0 +} + // File Data + __0: .text "jg" + .byte 0 + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] *((const byte*) main::point1_initials) ← memcpy(*((const byte*) $0), (number) 3) [ ] ( main:2 [ ] ) always clobbers reg byte a reg byte y +Statement [5] *((const byte*) SCREEN) ← (const byte) main::point1_x [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [6] *((const byte*) SCREEN+(byte) 1) ← *((const byte*) main::point1_initials) [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [7] *((const byte*) SCREEN+(byte) 2) ← *((const byte*) main::point1_initials+(byte) 1) [ ] ( main:2 [ ] ) always clobbers reg byte a + +REGISTER UPLIFT SCOPES +Uplift Scope [Point] +Uplift Scope [main] +Uplift Scope [] + +Uplifting [Point] best 59 combination +Uplifting [main] best 59 combination +Uplifting [] best 59 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Minimal struct with MemberUnwind behavior - array member and local initializer + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin +__bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +__b1_from___bbegin: + jmp __b1 + // @1 +__b1: + // [2] call main + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: + // main +main: { + .const point1_x = 2 + // [4] *((const byte*) main::point1_initials) ← memcpy(*((const byte*) $0), (number) 3) -- _deref_pbuc1=_deref_pbuc2_memcpy_vbuc3 + ldy #3 + !: + lda __0-1,y + sta point1_initials-1,y + dey + bne !- + // [5] *((const byte*) SCREEN) ← (const byte) main::point1_x -- _deref_pbuc1=vbuc2 + lda #point1_x + sta SCREEN + // [6] *((const byte*) SCREEN+(byte) 1) ← *((const byte*) main::point1_initials) -- _deref_pbuc1=_deref_pbuc2 + lda point1_initials + sta SCREEN+1 + // [7] *((const byte*) SCREEN+(byte) 2) ← *((const byte*) main::point1_initials+(byte) 1) -- _deref_pbuc1=_deref_pbuc2 + lda point1_initials+1 + sta SCREEN+2 + jmp __breturn + // main::@return + __breturn: + // [8] return + rts + point1_initials: .fill 3, 0 +} + // File Data + __0: .text "jg" + .byte 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __bend +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label __bbegin with __b1 +Removing instruction __bbegin: +Removing instruction __b1_from___bbegin: +Removing instruction __bend_from___b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __bend: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction __b1: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(const byte*) $0[(number) 3] = (string) "jg" +(label) @1 +(label) @begin +(label) @end +(const byte*) Point::initials[(number) 3] = { fill( 3, 0) } +(byte) Point::x +(const byte*) SCREEN = (byte*) 1024 +(void()) main() +(label) main::@return +(const byte*) main::point1_initials[(number) 3] = { fill( 3, 0) } +(const byte) main::point1_x = (byte) 2 + + + +FINAL ASSEMBLER +Score: 44 + + // File Comments +// Minimal struct with MemberUnwind behavior - array member and local initializer + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + .const point1_x = 2 + // point1 = { 2, "jg" } + // [4] *((const byte*) main::point1_initials) ← memcpy(*((const byte*) $0), (number) 3) -- _deref_pbuc1=_deref_pbuc2_memcpy_vbuc3 + ldy #3 + !: + lda __0-1,y + sta point1_initials-1,y + dey + bne !- + // SCREEN[0] = point1.x + // [5] *((const byte*) SCREEN) ← (const byte) main::point1_x -- _deref_pbuc1=vbuc2 + lda #point1_x + sta SCREEN + // SCREEN[1] = point1.initials[0] + // [6] *((const byte*) SCREEN+(byte) 1) ← *((const byte*) main::point1_initials) -- _deref_pbuc1=_deref_pbuc2 + lda point1_initials + sta SCREEN+1 + // SCREEN[2] = point1.initials[1] + // [7] *((const byte*) SCREEN+(byte) 2) ← *((const byte*) main::point1_initials+(byte) 1) -- _deref_pbuc1=_deref_pbuc2 + lda point1_initials+1 + sta SCREEN+2 + // main::@return + // } + // [8] return + rts + point1_initials: .fill 3, 0 +} + // File Data + __0: .text "jg" + .byte 0 + diff --git a/src/test/ref/struct-30.sym b/src/test/ref/struct-30.sym new file mode 100644 index 000000000..468f3148d --- /dev/null +++ b/src/test/ref/struct-30.sym @@ -0,0 +1,12 @@ +(const byte*) $0[(number) 3] = (string) "jg" +(label) @1 +(label) @begin +(label) @end +(const byte*) Point::initials[(number) 3] = { fill( 3, 0) } +(byte) Point::x +(const byte*) SCREEN = (byte*) 1024 +(void()) main() +(label) main::@return +(const byte*) main::point1_initials[(number) 3] = { fill( 3, 0) } +(const byte) main::point1_x = (byte) 2 + diff --git a/src/test/ref/struct-31.asm b/src/test/ref/struct-31.asm new file mode 100644 index 000000000..3e1499337 --- /dev/null +++ b/src/test/ref/struct-31.asm @@ -0,0 +1,14 @@ +// Minimal struct with MemberUnwind behavior - simple members and local initializer +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 +main: { + .const point1_x = 2 + .const point1_y = 3 + lda #point1_x + sta SCREEN + lda #point1_y + sta SCREEN+1 + rts +} diff --git a/src/test/ref/struct-31.cfg b/src/test/ref/struct-31.cfg new file mode 100644 index 000000000..a9a9926c1 --- /dev/null +++ b/src/test/ref/struct-31.cfg @@ -0,0 +1,18 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() + +(void()) main() +main: scope:[main] from @1 + [4] *((const byte*) SCREEN) ← (const byte) main::point1_x + [5] *((const byte*) SCREEN+(byte) 1) ← (const byte) main::point1_y + to:main::@return +main::@return: scope:[main] from main + [6] return + to:@return diff --git a/src/test/ref/struct-31.log b/src/test/ref/struct-31.log new file mode 100644 index 000000000..a35a55c93 --- /dev/null +++ b/src/test/ref/struct-31.log @@ -0,0 +1,264 @@ +Created struct value member variable (byte) main::point1_x +Created struct value member variable (byte) main::point1_y +Converted struct value to member variables (struct Point) main::point1 +Adding struct value member variable copy (byte) main::point1_x ← (byte) 2 +Adding struct value member variable copy (byte) main::point1_y ← (byte) 3 +Replacing struct member reference (struct Point) main::point1.x with member unwinding reference (byte) main::point1_x +Replacing struct member reference (struct Point) main::point1.y with member unwinding reference (byte) main::point1_y +Identified constant variable (byte) main::point1_x +Identified constant variable (byte) main::point1_y + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@1 + +(void()) main() +main: scope:[main] from @1 + *((const byte*) SCREEN + (number) 0) ← (const byte) main::point1_x + *((const byte*) SCREEN + (number) 1) ← (const byte) main::point1_y + to:main::@return +main::@return: scope:[main] from main + return + to:@return +@1: scope:[] from @begin + call main + to:@2 +@2: scope:[] from @1 + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(label) @1 +(label) @2 +(label) @begin +(label) @end +(byte) Point::x +(byte) Point::y +(const byte*) SCREEN = (byte*)(number) $400 +(void()) main() +(label) main::@return +(const byte) main::point1_x = (byte) 2 +(const byte) main::point1_y = (byte) 3 + +Adding number conversion cast (unumber) 0 in *((const byte*) SCREEN + (number) 0) ← (const byte) main::point1_x +Adding number conversion cast (unumber) 1 in *((const byte*) SCREEN + (number) 1) ← (const byte) main::point1_y +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast 1 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simplifying expression containing zero SCREEN in [0] *((const byte*) SCREEN + (byte) 0) ← (const byte) main::point1_x +Successful SSA optimization PassNSimplifyExpressionWithZero +Consolidated array index constant in *(SCREEN+1) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +CALL GRAPH +Calls in [] to main:2 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Culled Empty Block (label) @2 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() + +(void()) main() +main: scope:[main] from @1 + [4] *((const byte*) SCREEN) ← (const byte) main::point1_x + [5] *((const byte*) SCREEN+(byte) 1) ← (const byte) main::point1_y + to:main::@return +main::@return: scope:[main] from main + [6] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte) Point::x +(byte) Point::y +(void()) main() + +Initial phi equivalence classes +Complete equivalence classes + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Minimal struct with MemberUnwind behavior - simple members and local initializer + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin +__bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +__b1_from___bbegin: + jmp __b1 + // @1 +__b1: + // [2] call main + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: + // main +main: { + .const point1_x = 2 + .const point1_y = 3 + // [4] *((const byte*) SCREEN) ← (const byte) main::point1_x -- _deref_pbuc1=vbuc2 + lda #point1_x + sta SCREEN + // [5] *((const byte*) SCREEN+(byte) 1) ← (const byte) main::point1_y -- _deref_pbuc1=vbuc2 + lda #point1_y + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [6] return + rts +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] *((const byte*) SCREEN) ← (const byte) main::point1_x [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [5] *((const byte*) SCREEN+(byte) 1) ← (const byte) main::point1_y [ ] ( main:2 [ ] ) always clobbers reg byte a + +REGISTER UPLIFT SCOPES +Uplift Scope [Point] +Uplift Scope [main] +Uplift Scope [] + +Uplifting [Point] best 33 combination +Uplifting [main] best 33 combination +Uplifting [] best 33 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Minimal struct with MemberUnwind behavior - simple members and local initializer + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin +__bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +__b1_from___bbegin: + jmp __b1 + // @1 +__b1: + // [2] call main + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: + // main +main: { + .const point1_x = 2 + .const point1_y = 3 + // [4] *((const byte*) SCREEN) ← (const byte) main::point1_x -- _deref_pbuc1=vbuc2 + lda #point1_x + sta SCREEN + // [5] *((const byte*) SCREEN+(byte) 1) ← (const byte) main::point1_y -- _deref_pbuc1=vbuc2 + lda #point1_y + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [6] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __bend +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label __bbegin with __b1 +Removing instruction __bbegin: +Removing instruction __b1_from___bbegin: +Removing instruction __bend_from___b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __bend: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction __b1: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(byte) Point::x +(byte) Point::y +(const byte*) SCREEN = (byte*) 1024 +(void()) main() +(label) main::@return +(const byte) main::point1_x = (byte) 2 +(const byte) main::point1_y = (byte) 3 + + + +FINAL ASSEMBLER +Score: 18 + + // File Comments +// Minimal struct with MemberUnwind behavior - simple members and local initializer + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + .const point1_x = 2 + .const point1_y = 3 + // SCREEN[0] = point1.x + // [4] *((const byte*) SCREEN) ← (const byte) main::point1_x -- _deref_pbuc1=vbuc2 + lda #point1_x + sta SCREEN + // SCREEN[1] = point1.y + // [5] *((const byte*) SCREEN+(byte) 1) ← (const byte) main::point1_y -- _deref_pbuc1=vbuc2 + lda #point1_y + sta SCREEN+1 + // main::@return + // } + // [6] return + rts +} + // File Data + diff --git a/src/test/ref/struct-31.sym b/src/test/ref/struct-31.sym new file mode 100644 index 000000000..f9ce304b7 --- /dev/null +++ b/src/test/ref/struct-31.sym @@ -0,0 +1,11 @@ +(label) @1 +(label) @begin +(label) @end +(byte) Point::x +(byte) Point::y +(const byte*) SCREEN = (byte*) 1024 +(void()) main() +(label) main::@return +(const byte) main::point1_x = (byte) 2 +(const byte) main::point1_y = (byte) 3 +