diff --git a/src/main/java/dk/camelot64/kickc/CompileLog.java b/src/main/java/dk/camelot64/kickc/CompileLog.java index 9c9490d13..51672fa46 100644 --- a/src/main/java/dk/camelot64/kickc/CompileLog.java +++ b/src/main/java/dk/camelot64/kickc/CompileLog.java @@ -35,7 +35,7 @@ public class CompileLog { /** * Should Memory usage be verbose */ - private boolean verboseMemoryUsage = false; + private boolean verboseSizeInfo = false; /** * Should loop unrolling be verbose. @@ -101,17 +101,17 @@ public class CompileLog { return log; } - public CompileLog verboseMemoryUsage() { - setVerboseMemoryUsage(true); + public CompileLog verboseSizeInfo() { + setVerboseSizeInfo(true); return this; } - public boolean isVerboseMemoryUsage() { - return verboseMemoryUsage; + public boolean isVerboseSizeInfo() { + return verboseSizeInfo; } - public void setVerboseMemoryUsage(boolean verboseMemoryUsage) { - this.verboseMemoryUsage = verboseMemoryUsage; + public void setVerboseSizeInfo(boolean verboseSizeInfo) { + this.verboseSizeInfo = verboseSizeInfo; } public CompileLog verboseSsaSourceCode() { diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index eb0ddcdb0..99d6b57c5 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -321,7 +321,7 @@ public class Compiler { private void pass2Optimize() { - if(getLog().isVerboseMemoryUsage()) { + if(getLog().isVerboseSizeInfo()) { getLog().append(program.getSizeInfo()); } @@ -373,7 +373,7 @@ public class Compiler { private void pass2InlineConstants() { - if(getLog().isVerboseMemoryUsage()) { + if(getLog().isVerboseSizeInfo()) { getLog().append(program.getSizeInfo()); } @@ -445,7 +445,7 @@ public class Compiler { private void pass3Analysis() { - if(getLog().isVerboseMemoryUsage()) { + if(getLog().isVerboseSizeInfo()) { getLog().append(program.getSizeInfo()); } @@ -485,6 +485,10 @@ public class Compiler { new PassNBlockSequencePlanner(program).step(); new Pass3AddNopBeforeCallOns(program).generate(); new PassNStatementIndices(program).execute(); + + // Handle calling convention stack + new PassNCallingConventionStack(program).execute(); + program.clearCallGraph(); program.clearStatementInfos(); program.clearVariableReferenceInfos(); @@ -499,7 +503,7 @@ public class Compiler { private void pass4RegisterAllocation() { - if(getLog().isVerboseMemoryUsage()) { + if(getLog().isVerboseSizeInfo()) { getLog().append(program.getSizeInfo()); } @@ -554,7 +558,7 @@ public class Compiler { } while(change); getLog().append(program.getRegisterPotentials().toString()); - if(getLog().isVerboseMemoryUsage()) { + if(getLog().isVerboseSizeInfo()) { getLog().append(program.getSizeInfo()); } diff --git a/src/main/java/dk/camelot64/kickc/KickC.java b/src/main/java/dk/camelot64/kickc/KickC.java index cd00a421d..a2ed67362 100644 --- a/src/main/java/dk/camelot64/kickc/KickC.java +++ b/src/main/java/dk/camelot64/kickc/KickC.java @@ -90,8 +90,8 @@ public class KickC implements Callable { @CommandLine.Option(names = {"-voptimize"}, description = "Verbosity Option. Control Flow Graph Optimization.") private boolean verboseSSAOptimize = false; - @CommandLine.Option(names = {"-vmemory"}, description = "Verbosity Option. Compiler Data Structure Sizes.") - private boolean verboseMemory = false; + @CommandLine.Option(names = {"-vsizeinfo"}, description = "Verbosity Option. Compiler Data Structure Size Information.") + private boolean verboseSizeInfo = false; @CommandLine.Option(names = {"-vnonoptimize"}, description = "Verbosity Option. Choices not to optimize.") private boolean verboseNonOptimization = false; @@ -380,8 +380,8 @@ public class KickC implements Callable { compiler.getLog().setVerboseSSAOptimize(true); compiler.getLog().setSysOut(true); } - if(verboseMemory) { - compiler.getLog().setVerboseMemoryUsage(true); + if(verboseSizeInfo) { + compiler.getLog().setVerboseSizeInfo(true); compiler.getLog().setSysOut(true); } if(verboseNonOptimization) { diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java index f4750f2ad..958dca187 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java @@ -233,7 +233,6 @@ public class AsmFragmentInstanceSpecFactory { * @return The bound name of the value. If the value has already been bound the existing bound name is returned. */ public String bind(Value value, SymbolType castType) { - if(value instanceof CastValue) { CastValue cast = (CastValue) value; SymbolType toType = cast.getToType(); @@ -357,8 +356,11 @@ public class AsmFragmentInstanceSpecFactory { String name = "la" + nextLabelIdx++; bind(name, value); return name; + } else if(value instanceof ParamStackValue) { + // TODO: Handle different parameter types! + return "_stackbyte_"+bind(((ParamStackValue) value).getStackOffset()); } - throw new RuntimeException("Binding of value type not supported " + value); + throw new RuntimeException("Binding of value type not supported " + value.toString(program)); } /** diff --git a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java index 98e55de9a..a1be30088 100644 --- a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java +++ b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java @@ -775,6 +775,26 @@ public interface ProgramValue { } + /** Value inside a parameter stack value . */ + class ProgramValueParamStackValue implements ProgramValue { + private final ParamStackValue paramValue; + + ProgramValueParamStackValue(ParamStackValue paramValue) { + this.paramValue = paramValue; + } + + @Override + public Value get() { + return paramValue.getStackOffset(); + } + + @Override + public void set(Value val) { + paramValue.setStackOffset((ConstantRef) val); + } + + } + /** * Pointer inside a pointer derefence value. */ diff --git a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValueIterator.java b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValueIterator.java index 3b0809096..36b67466a 100644 --- a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValueIterator.java +++ b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValueIterator.java @@ -236,6 +236,8 @@ public class ProgramValueIterator { subValues.add(new ProgramValue.ProgramValueLValueIntermediateVariable((LvalueIntermediate) value)); } else if(value instanceof ParamValue) { subValues.add(new ProgramValue.ProgramValueParamValue((ParamValue) value)); + } else if(value instanceof ParamStackValue) { + subValues.add(new ProgramValue.ProgramValueParamStackValue((ParamStackValue) value)); } else if(value == null || value instanceof VariableRef || value instanceof VariableVersion || diff --git a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInference.java b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInference.java index d1ba170b0..b934641b0 100644 --- a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInference.java +++ b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInference.java @@ -116,6 +116,8 @@ public class SymbolTypeInference { return ((StructZero)rValue).getTypeStruct(); } else if(rValue instanceof ParamValue) { return inferType(symbols, ((ParamValue) rValue).getParameter()); + } else if(rValue instanceof ParamStackValue) { + return SymbolType.BYTE; } else if(rValue instanceof StructUnwoundPlaceholder) { return ((StructUnwoundPlaceholder) rValue).getTypeStruct(); } diff --git a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeStruct.java b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeStruct.java index ea10d5726..5262ad81b 100644 --- a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeStruct.java +++ b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeStruct.java @@ -77,7 +77,7 @@ public class SymbolTypeStruct implements SymbolType { } else { return memberType.getSizeBytes(); } - throw new InternalError("Memeber type not handled "+memberType); + throw new InternalError("Member type not handled "+memberType); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/values/ParamStackValue.java b/src/main/java/dk/camelot64/kickc/model/values/ParamStackValue.java new file mode 100644 index 000000000..6b2ee4a3e --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/model/values/ParamStackValue.java @@ -0,0 +1,28 @@ +package dk.camelot64.kickc.model.values; + +import dk.camelot64.kickc.model.Program; + +/** The value passed into a function for a specific parameter using the stack. */ +public class ParamStackValue implements RValue { + + /** The constant holding the stack offset of the parameter. */ + private ConstantRef stackOffset; + + public ParamStackValue(ConstantRef stackOffset) { + this.stackOffset = stackOffset; + } + + public ConstantRef getStackOffset() { + return stackOffset; + } + + public void setStackOffset(ConstantRef stackOffset) { + this.stackOffset = stackOffset; + } + + @Override + public String toString(Program program) { + return "paramstack("+stackOffset+")"; + } + +} diff --git a/src/main/java/dk/camelot64/kickc/model/values/ParamValue.java b/src/main/java/dk/camelot64/kickc/model/values/ParamValue.java index e5a4ecf19..94159de68 100644 --- a/src/main/java/dk/camelot64/kickc/model/values/ParamValue.java +++ b/src/main/java/dk/camelot64/kickc/model/values/ParamValue.java @@ -8,7 +8,7 @@ import dk.camelot64.kickc.model.Program; public class ParamValue implements RValue { /** The (unversioned) parameter variable. */ - VariableRef parameter; + private VariableRef parameter; public ParamValue(VariableRef parameter) { this.parameter = parameter; diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNCallingConventionStack.java b/src/main/java/dk/camelot64/kickc/passes/PassNCallingConventionStack.java new file mode 100644 index 000000000..233ce4c8f --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/PassNCallingConventionStack.java @@ -0,0 +1,104 @@ +package dk.camelot64.kickc.passes; + +import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.iterator.ProgramValueIterator; +import dk.camelot64.kickc.model.symbols.*; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.values.*; + +import java.util.HashMap; +import java.util.Map; + +/** Handle calling convention {@link Procedure.CallingConvension#STACK_CALL} by converting the making control flow graph and symbols calling convention specific. */ +public class PassNCallingConventionStack extends Pass2SsaOptimization { + + public PassNCallingConventionStack(Program program) { + super(program); + } + + @Override + public boolean step() { + // Offset-constants for all stack-call parameters + Map offsetConstants = new HashMap<>(); + + // Introduce STACK_OFFSET constants + for(Procedure procedure : getScope().getAllProcedures(true)) { + if(Procedure.CallingConvension.STACK_CALL.equals(procedure.getCallingConvension())) { + for(Variable parameter : procedure.getParameters()) { + ConstantRef parameterOffsetConstant = getParameterOffsetConstant(procedure, parameter, getScope()); + offsetConstants.put(parameter.getRef(), parameterOffsetConstant); + } + } + } + + //TODO: Add global STACK_BASE constant + + if(offsetConstants.size() > 0) { + // Convert ParamValue to ParamStackValue + ProgramValueIterator.execute(getGraph(), (programValue, currentStmt, stmtIt, currentBlock) -> { + if(programValue.get() instanceof ParamValue) { + // Convert ParamValues to calling-convention specific param-value + ParamValue paramValue = (ParamValue) programValue.get(); + VariableRef parameterRef = paramValue.getParameter(); + if(offsetConstants.containsKey(parameterRef)) { + ParamStackValue paramStackValue = new ParamStackValue(offsetConstants.get(parameterRef)); + programValue.set(paramStackValue); + getLog().append("Calling convention " + Procedure.CallingConvension.STACK_CALL + " replacing " + paramValue.toString(getProgram()) + " with " + paramStackValue.toString(getProgram())); + } + } + }); + } + return false; + } + + /** + * Get the constant variable containing the (byte) index of a parameter on the stack + * + * @param procedure The procedure + * @param procedure The parameter + * @param programScope The program scope (used for finding/adding the constant). + * @return The constant variable + */ + public static ConstantRef getParameterOffsetConstant(Procedure procedure, Variable parameter, ProgramScope programScope) { + String paramOffsetConstantName = getParameterOffsetConstantName(parameter.getName()); + ConstantVar paramOffsetConstant = procedure.getConstant(paramOffsetConstantName); + if(paramOffsetConstant == null) { + // Constant not found - create it + long paramByteOffset = getParameterByteOffset(procedure, parameter, programScope); + paramOffsetConstant = new ConstantVar(paramOffsetConstantName, procedure, SymbolType.BYTE, new ConstantInteger(paramByteOffset & 0xff, SymbolType.BYTE), Scope.SEGMENT_DATA_DEFAULT); + procedure.add(paramOffsetConstant); + } + return paramOffsetConstant.getRef(); + } + + /** + * Get the name of the constant variable containing the (byte) offset of a specific parameter on the stack + * + * @param parameterName The name of the struct member + * @return The name of the constant + */ + private static String getParameterOffsetConstantName(String parameterName) { + // TODO: Maybe use asmName? + return "OFFSET_STACK_" + parameterName.toUpperCase(); + } + + /** + * Get the number of bytes that a parameter is offset on the stack + * + * @param parameter The parameter to find offset for + * @return The byte offset of the start of the member data + */ + public static long getParameterByteOffset(Procedure procedure, Variable parameter, ProgramScope programScope) { + long byteOffset = 0; + for(Variable procedureParameter : procedure.getParameters()) { + if(parameter.equals(procedureParameter)) { + break; + } else { + // TODO: Consider hos passing a struct should handle inline arrays byteOffset += SymbolTypeStruct.getMemberSizeBytes(parameter.getType(), programScope); + byteOffset += parameter.getType().getSizeBytes(); + } + } + return byteOffset; + } + +}