From a63adec0f02da7c420dc0c51ded5f96cd6a8d275 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Wed, 27 May 2020 09:05:20 +0200 Subject: [PATCH] Fixed problem with incorrect parenthesising of non-associative binary operators in constant expressions. Closes #450 --- .../dk/camelot64/kickc/asm/AsmFormat.java | 24 +- .../model/operators/OperatorAssignment.java | 2 +- .../kickc/model/operators/OperatorBinary.java | 9 +- .../model/operators/OperatorBitwiseAnd.java | 2 +- .../model/operators/OperatorBitwiseOr.java | 2 +- .../model/operators/OperatorBitwiseXor.java | 2 +- .../kickc/model/operators/OperatorDWord.java | 2 +- .../model/operators/OperatorDerefIdx.java | 2 +- .../kickc/model/operators/OperatorDivide.java | 2 +- .../kickc/model/operators/OperatorEqual.java | 2 +- .../model/operators/OperatorGreaterThan.java | 2 +- .../operators/OperatorGreaterThanEqual.java | 2 +- .../model/operators/OperatorLessThan.java | 2 +- .../operators/OperatorLessThanEqual.java | 2 +- .../model/operators/OperatorLogicAnd.java | 2 +- .../model/operators/OperatorLogicOr.java | 2 +- .../kickc/model/operators/OperatorMinus.java | 2 +- .../kickc/model/operators/OperatorModulo.java | 2 +- .../model/operators/OperatorMultiply.java | 2 +- .../model/operators/OperatorNotEqual.java | 2 +- .../kickc/model/operators/OperatorPlus.java | 2 +- .../model/operators/OperatorSetHigh.java | 2 +- .../kickc/model/operators/OperatorSetLow.java | 2 +- .../model/operators/OperatorShiftLeft.java | 2 +- .../model/operators/OperatorShiftRight.java | 2 +- .../kickc/model/operators/OperatorWord.java | 2 +- .../dk/camelot64/kickc/test/TestPrograms.java | 12 + src/test/kc/examples/nes/nes-demo.c | 6 +- src/test/kc/minus-precedence-problem.c | 7 + src/test/kc/problem-ma-var-overwrite.c | 25 + src/test/ref/examples/nes/nes-demo.asm | 2 +- src/test/ref/examples/nes/nes-demo.log | 10 +- src/test/ref/examples/nes/nes-demo.sym | 2 +- src/test/ref/minus-precedence-problem.asm | 12 + src/test/ref/minus-precedence-problem.cfg | 17 + src/test/ref/minus-precedence-problem.log | 222 ++++++++ src/test/ref/minus-precedence-problem.sym | 8 + src/test/ref/problem-ma-var-overwrite.asm | 61 ++ src/test/ref/problem-ma-var-overwrite.cfg | 31 + src/test/ref/problem-ma-var-overwrite.log | 536 ++++++++++++++++++ src/test/ref/problem-ma-var-overwrite.sym | 21 + 41 files changed, 1011 insertions(+), 42 deletions(-) create mode 100644 src/test/kc/minus-precedence-problem.c create mode 100644 src/test/kc/problem-ma-var-overwrite.c create mode 100644 src/test/ref/minus-precedence-problem.asm create mode 100644 src/test/ref/minus-precedence-problem.cfg create mode 100644 src/test/ref/minus-precedence-problem.log create mode 100644 src/test/ref/minus-precedence-problem.sym create mode 100644 src/test/ref/problem-ma-var-overwrite.asm create mode 100644 src/test/ref/problem-ma-var-overwrite.cfg create mode 100644 src/test/ref/problem-ma-var-overwrite.log create mode 100644 src/test/ref/problem-ma-var-overwrite.sym diff --git a/src/main/java/dk/camelot64/kickc/asm/AsmFormat.java b/src/main/java/dk/camelot64/kickc/asm/AsmFormat.java index b98c1d809..173b1c7a2 100644 --- a/src/main/java/dk/camelot64/kickc/asm/AsmFormat.java +++ b/src/main/java/dk/camelot64/kickc/asm/AsmFormat.java @@ -89,6 +89,7 @@ public class AsmFormat { * @return */ private static String getAsmConstantBinary(Program program, ConstantValue left, OperatorBinary operator, ConstantValue right, ScopeRef codeScope) { + if(Operators.MODULO.equals(operator)) { // Remainder operator % not supported by KickAss - use modulo function instead return "mod(" + @@ -96,11 +97,20 @@ public class AsmFormat { "," + getAsmConstant(program, right, operator.getPrecedence(), codeScope) + ")"; - } else { - return getAsmConstant(program, left, operator.getPrecedence(), codeScope) + - operator.getOperator() + - getAsmConstant(program, right, operator.getPrecedence(), codeScope); } + + // Handle non-associative operators - only handle right side since parser is left-associativeA-B-C = (A-B)-C + boolean rightParenthesis = false; + if(!operator.isAssociative()) { + if(right instanceof ConstantBinary && ((ConstantBinary) right).getOperator().equals(operator)) { + // Right sub-expression is also binary with the same non-associative operator + rightParenthesis = true; + } + } + + return getAsmConstant(program, left, operator.getPrecedence(), codeScope) + + operator.getOperator() + + (rightParenthesis ? "(" : "") + getAsmConstant(program, right, operator.getPrecedence(), codeScope) + (rightParenthesis ? ")" : ""); } /** @@ -220,9 +230,9 @@ public class AsmFormat { } else if(Operators.POS.equals(operator)) { return getAsmConstant(program, operand, outerPrecedence, codeScope); } else if(Operators.NEG.equals(operator) && operand instanceof ConstantUnary) { - return operator.getOperator() + "(" +getAsmConstant(program, operand, operator.getPrecedence(), codeScope)+ ")"; - } else if(Operators.NEG.equals(operator) && operand instanceof ConstantInteger && ((ConstantInteger) operand).getInteger()<0) { - return operator.getOperator() + "(" +getAsmConstant(program, operand, operator.getPrecedence(), codeScope)+ ")"; + return operator.getOperator() + "(" + getAsmConstant(program, operand, operator.getPrecedence(), codeScope) + ")"; + } else if(Operators.NEG.equals(operator) && operand instanceof ConstantInteger && ((ConstantInteger) operand).getInteger() < 0) { + return operator.getOperator() + "(" + getAsmConstant(program, operand, operator.getPrecedence(), codeScope) + ")"; } else { return operator.getOperator() + getAsmConstant(program, operand, operator.getPrecedence(), codeScope); diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorAssignment.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorAssignment.java index 9e84e1b3d..98a8d379f 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorAssignment.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorAssignment.java @@ -8,7 +8,7 @@ import dk.camelot64.kickc.model.values.ConstantLiteral; public class OperatorAssignment extends OperatorBinary { public OperatorAssignment(int precedence) { - super("=", "_assign_", precedence); + super("=", "_assign_", precedence, false); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorBinary.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorBinary.java index 9a6a79d30..2055a15eb 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorBinary.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorBinary.java @@ -6,8 +6,12 @@ import dk.camelot64.kickc.model.values.ConstantLiteral; /** A binary expression operator */ public abstract class OperatorBinary extends Operator { - public OperatorBinary(String operator, String asmOperator, int precedence) { + /** Is the operator associative, meaning that (A+B)+C = A+(B+C). */ + private boolean associative; + + public OperatorBinary(String operator, String asmOperator, int precedence, boolean associative) { super(operator, asmOperator, Type.BINARY, precedence); + this.associative = associative; } /** @@ -26,4 +30,7 @@ public abstract class OperatorBinary extends Operator { */ public abstract SymbolType inferType(SymbolType left, SymbolType right); + public boolean isAssociative() { + return associative; + } } diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseAnd.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseAnd.java index 5d44e8a60..4c75aaf9d 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseAnd.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseAnd.java @@ -11,7 +11,7 @@ import dk.camelot64.kickc.model.values.ConstantPointer; public class OperatorBitwiseAnd extends OperatorBinary { public OperatorBitwiseAnd(int precedence) { - super("&", "_band_", precedence); + super("&", "_band_", precedence, true); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseOr.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseOr.java index 4b08dacac..78ea4e7ac 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseOr.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseOr.java @@ -14,7 +14,7 @@ import dk.camelot64.kickc.model.values.ConstantPointer; public class OperatorBitwiseOr extends OperatorBinary { public OperatorBitwiseOr(int precedence) { - super("|", "_bor_", precedence); + super("|", "_bor_", precedence, true); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseXor.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseXor.java index 39dba810e..1e149c918 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseXor.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorBitwiseXor.java @@ -11,7 +11,7 @@ import dk.camelot64.kickc.model.values.ConstantPointer; public class OperatorBitwiseXor extends OperatorBinary { public OperatorBitwiseXor(int precedence) { - super("^", "_bxor_", precedence); + super("^", "_bxor_", precedence, true); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorDWord.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorDWord.java index 9ef7fc18d..55d873c15 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorDWord.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorDWord.java @@ -11,7 +11,7 @@ import dk.camelot64.kickc.model.values.ConstantLiteral; public class OperatorDWord extends OperatorBinary { public OperatorDWord(int precedence) { - super("dw=", "_dword_", precedence); + super("dw=", "_dword_", precedence, false); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorDerefIdx.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorDerefIdx.java index 834e5c66b..d5fa57590 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorDerefIdx.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorDerefIdx.java @@ -9,7 +9,7 @@ import dk.camelot64.kickc.model.values.ConstantLiteral; public class OperatorDerefIdx extends OperatorBinary { public OperatorDerefIdx(int precedence) { - super("*idx=", "_derefidx_", precedence); + super("*idx=", "_derefidx_", precedence, false); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorDivide.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorDivide.java index 42c4297d6..12124c2a8 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorDivide.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorDivide.java @@ -10,7 +10,7 @@ import dk.camelot64.kickc.model.values.ConstantPointer; public class OperatorDivide extends OperatorBinary { public OperatorDivide(int precedence) { - super("/", "_div_", precedence); + super("/", "_div_", precedence, false); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorEqual.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorEqual.java index de755ff97..4b4be56cc 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorEqual.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorEqual.java @@ -12,7 +12,7 @@ import java.util.Objects; public class OperatorEqual extends OperatorBinary { public OperatorEqual(int precedence) { - super("==", "_eq_", precedence); + super("==", "_eq_", precedence, false); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorGreaterThan.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorGreaterThan.java index 87084a4f4..e59ecf906 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorGreaterThan.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorGreaterThan.java @@ -8,7 +8,7 @@ import dk.camelot64.kickc.model.values.*; public class OperatorGreaterThan extends OperatorBinary { public OperatorGreaterThan(int precedence) { - super(">", "_gt_", precedence); + super(">", "_gt_", precedence, false); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorGreaterThanEqual.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorGreaterThanEqual.java index 1808dc617..5f6778bb2 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorGreaterThanEqual.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorGreaterThanEqual.java @@ -8,7 +8,7 @@ import dk.camelot64.kickc.model.values.*; public class OperatorGreaterThanEqual extends OperatorBinary { public OperatorGreaterThanEqual(int precedence) { - super(">=", "_ge_", precedence); + super(">=", "_ge_", precedence, false); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorLessThan.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorLessThan.java index db528c5fe..6b2dee653 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorLessThan.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorLessThan.java @@ -10,7 +10,7 @@ import dk.camelot64.kickc.model.values.ConstantLiteral; public class OperatorLessThan extends OperatorBinary { public OperatorLessThan(int precedence) { - super("<", "_lt_", precedence); + super("<", "_lt_", precedence, false); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorLessThanEqual.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorLessThanEqual.java index e84ecc471..9b2e4d4b1 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorLessThanEqual.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorLessThanEqual.java @@ -8,7 +8,7 @@ import dk.camelot64.kickc.model.values.*; public class OperatorLessThanEqual extends OperatorBinary { public OperatorLessThanEqual(int precedence) { - super("<=", "_le_", precedence); + super("<=", "_le_", precedence, false); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorLogicAnd.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorLogicAnd.java index 0088ec944..a22d2253d 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorLogicAnd.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorLogicAnd.java @@ -9,7 +9,7 @@ import dk.camelot64.kickc.model.values.ConstantLiteral; public class OperatorLogicAnd extends OperatorBinary { public OperatorLogicAnd(int precedence) { - super("&&", "_and_", precedence); + super("&&", "_and_", precedence, true); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorLogicOr.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorLogicOr.java index 5d5b92e68..336be99c0 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorLogicOr.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorLogicOr.java @@ -9,7 +9,7 @@ import dk.camelot64.kickc.model.values.ConstantLiteral; public class OperatorLogicOr extends OperatorBinary { public OperatorLogicOr(int precedence) { - super("||", "_or_", precedence); + super("||", "_or_", precedence, true); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorMinus.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorMinus.java index fdaa13292..676ec53eb 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorMinus.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorMinus.java @@ -11,7 +11,7 @@ import dk.camelot64.kickc.model.values.*; public class OperatorMinus extends OperatorBinary { public OperatorMinus(int precedence) { - super("-", "_minus_", precedence); + super("-", "_minus_", precedence, false); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorModulo.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorModulo.java index 7235c3fdc..399c19c2e 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorModulo.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorModulo.java @@ -10,7 +10,7 @@ import dk.camelot64.kickc.model.values.ConstantPointer; public class OperatorModulo extends OperatorBinary { public OperatorModulo(int precedence) { - super("%", "_mod_", precedence); + super("%", "_mod_", precedence, false); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorMultiply.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorMultiply.java index 3a8920008..ec24f32d2 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorMultiply.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorMultiply.java @@ -9,7 +9,7 @@ import dk.camelot64.kickc.model.values.ConstantLiteral; public class OperatorMultiply extends OperatorBinary { public OperatorMultiply(int precedence) { - super("*", "_mul_", precedence); + super("*", "_mul_", precedence, true); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorNotEqual.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorNotEqual.java index fb133cfab..1ca81c827 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorNotEqual.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorNotEqual.java @@ -10,7 +10,7 @@ import java.util.Objects; public class OperatorNotEqual extends OperatorBinary { public OperatorNotEqual(int precedence) { - super("!=", "_neq_", precedence); + super("!=", "_neq_", precedence, false); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorPlus.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorPlus.java index 3f34fad90..6d6cd8405 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorPlus.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorPlus.java @@ -12,7 +12,7 @@ import dk.camelot64.kickc.model.values.*; public class OperatorPlus extends OperatorBinary { public OperatorPlus(int precedence) { - super("+", "_plus_", precedence); + super("+", "_plus_", precedence, true); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorSetHigh.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorSetHigh.java index cbd591d80..72cc4fb03 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorSetHigh.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorSetHigh.java @@ -9,7 +9,7 @@ import dk.camelot64.kickc.model.values.ConstantLiteral; public class OperatorSetHigh extends OperatorBinary { public OperatorSetHigh(int precedence) { - super("hi=", "_sethi_", precedence); + super("hi=", "_sethi_", precedence, false); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorSetLow.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorSetLow.java index 79b07ae47..4f8a93c9b 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorSetLow.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorSetLow.java @@ -9,7 +9,7 @@ import dk.camelot64.kickc.model.values.ConstantLiteral; public class OperatorSetLow extends OperatorBinary { public OperatorSetLow(int precedence) { - super("lo=", "_setlo_", precedence); + super("lo=", "_setlo_", precedence, false); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorShiftLeft.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorShiftLeft.java index 5d84970ef..475112268 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorShiftLeft.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorShiftLeft.java @@ -9,7 +9,7 @@ import dk.camelot64.kickc.model.values.ConstantLiteral; public class OperatorShiftLeft extends OperatorBinary { public OperatorShiftLeft(int precedence) { - super("<<", "_rol_", precedence); + super("<<", "_rol_", precedence, false); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorShiftRight.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorShiftRight.java index 352dae6d5..6701182aa 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorShiftRight.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorShiftRight.java @@ -9,7 +9,7 @@ import dk.camelot64.kickc.model.values.ConstantLiteral; public class OperatorShiftRight extends OperatorBinary { public OperatorShiftRight(int precedence) { - super(">>", "_ror_", precedence); + super(">>", "_ror_", precedence, false); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorWord.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorWord.java index 01f96476c..809aba178 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorWord.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorWord.java @@ -10,7 +10,7 @@ import dk.camelot64.kickc.model.values.ConstantLiteral; public class OperatorWord extends OperatorBinary { public OperatorWord(int precedence) { - super("w=", "_word_", precedence); + super("w=", "_word_", precedence, false); } @Override diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 6fb56e606..efe2f600c 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -44,6 +44,18 @@ public class TestPrograms { public TestPrograms() { } + + @Test + public void testProblemMaVarOverwrite() throws IOException, URISyntaxException { + compileAndCompare("problem-ma-var-overwrite.c"); + } + + @Test + public void testMinusPrecedenceProblem() throws IOException, URISyntaxException { + compileAndCompare("minus-precedence-problem.c"); + } + + @Test public void testNesDemo() throws IOException, URISyntaxException { compileAndCompare("examples/nes/nes-demo.c"); diff --git a/src/test/kc/examples/nes/nes-demo.c b/src/test/kc/examples/nes/nes-demo.c index e64269e96..d08b00368 100644 --- a/src/test/kc/examples/nes/nes-demo.c +++ b/src/test/kc/examples/nes/nes-demo.c @@ -95,9 +95,9 @@ struct SpriteData SPRITES[] = { // Color Palette char PALETTE[0x20] = { // Background palettes - 0x0f, 0x2d, 0x08, 0x18, - 0x0f, 0x06, 0x15, 0x36, - 0x0f, 0x39, 0x4a, 0x5b, + 0x11, 0x2d, 0x08, 0x18, + 0x11, 0x06, 0x15, 0x36, + 0x11, 0x39, 0x4a, 0x5b, 0x0f, 0x3d, 0x4e, 0x5f, // Sprite palettes (selected by the attribute bits 0-1 of the sprites) 0x11, 0x0f, 0x30, 0x08, // Goomba upper colors diff --git a/src/test/kc/minus-precedence-problem.c b/src/test/kc/minus-precedence-problem.c new file mode 100644 index 000000000..94964ccc1 --- /dev/null +++ b/src/test/kc/minus-precedence-problem.c @@ -0,0 +1,7 @@ +const unsigned char matrixSize = 8; +const unsigned char matrixSizeMask = 255 - (matrixSize - 1); + +void main(void) +{ + *((unsigned char *)0x400) = matrixSizeMask; +} diff --git a/src/test/kc/problem-ma-var-overwrite.c b/src/test/kc/problem-ma-var-overwrite.c new file mode 100644 index 000000000..c20e1aac6 --- /dev/null +++ b/src/test/kc/problem-ma-var-overwrite.c @@ -0,0 +1,25 @@ +// Demonstrates that a local __ma variable overwrites a parameter§ + +unsigned char *volatile h1; // This must be volatile because is used in an interrupt routine... + +void test(unsigned char *videoMem, unsigned char *colorMem, unsigned char *other) +{ + unsigned char *diff; + __ma unsigned char *dst; // This must be declared as __ma because is used in an assembly routine... + + + diff = colorMem - videoMem; + dst = other + ((unsigned int)diff); + dst[0] = 1; + + asm { + ldy #0 + lda #1 + sta (dst),y + } +} + +void main(void) +{ + test(h1, 0xD800, 0xC000); +} diff --git a/src/test/ref/examples/nes/nes-demo.asm b/src/test/ref/examples/nes/nes-demo.asm index f7fd6efd3..bc007f591 100644 --- a/src/test/ref/examples/nes/nes-demo.asm +++ b/src/test/ref/examples/nes/nes-demo.asm @@ -543,7 +543,7 @@ readJoy1: { // Sprite Data SPRITES: .byte $96, $36, 2, $c, $96, $37, 2, $14, $9e, $38, 2, $c, $9e, $39, 2, $14, $96, $70, 0, $48, $96, $71, 0, $50, $9e, $72, 1, $48, $9e, $73, 1, $50 // Color Palette - PALETTE: .byte $f, $2d, 8, $18, $f, 6, $15, $36, $f, $39, $4a, $5b, $f, $3d, $4e, $5f, $11, $f, $30, 8, $11, $f, $18, 8, $11, $30, $37, $1a, $f, $f, $f, $f + PALETTE: .byte $11, $2d, 8, $18, $11, 6, $15, $36, $11, $39, $4a, $5b, $f, $3d, $4e, $5f, $11, $f, $30, 8, $11, $f, $18, 8, $11, $30, $37, $1a, $f, $f, $f, $f .segment Tiles TILES: .import binary "smb1_chr.bin" diff --git a/src/test/ref/examples/nes/nes-demo.log b/src/test/ref/examples/nes/nes-demo.log index 3afd19af2..0effcb1d4 100644 --- a/src/test/ref/examples/nes/nes-demo.log +++ b/src/test/ref/examples/nes/nes-demo.log @@ -627,7 +627,7 @@ SYMBOL TABLE SSA (const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS = (byte) 2 (const byte) OFFSET_STRUCT_SPRITEDATA_X = (byte) 3 (const byte) OFFSET_STRUCT_SPRITEDATA_Y = (byte) 0 -(const byte*) PALETTE[(number) $20] = { (byte) $f, (byte) $2d, (byte) 8, (byte) $18, (byte) $f, (byte) 6, (byte) $15, (byte) $36, (byte) $f, (byte) $39, (byte) $4a, (byte) $5b, (byte) $f, (byte) $3d, (byte) $4e, (byte) $5f, (byte) $11, (byte) $f, (byte) $30, (byte) 8, (byte) $11, (byte) $f, (byte) $18, (byte) 8, (byte) $11, (byte) $30, (byte) $37, (byte) $1a, (byte) $f, (byte) $f, (byte) $f, (byte) $f } +(const byte*) PALETTE[(number) $20] = { (byte) $11, (byte) $2d, (byte) 8, (byte) $18, (byte) $11, (byte) 6, (byte) $15, (byte) $36, (byte) $11, (byte) $39, (byte) $4a, (byte) $5b, (byte) $f, (byte) $3d, (byte) $4e, (byte) $5f, (byte) $11, (byte) $f, (byte) $30, (byte) 8, (byte) $11, (byte) $f, (byte) $18, (byte) 8, (byte) $11, (byte) $30, (byte) $37, (byte) $1a, (byte) $f, (byte) $f, (byte) $f, (byte) $f } (const struct RICOH_2C02*) PPU = (struct RICOH_2C02*)(number) $2000 (const nomodify byte*) PPU_ATTRIBUTE_TABLE_0 = (byte*)(number) $23c0 (const nomodify byte*) PPU_NAME_TABLE_0 = (byte*)(number) $2000 @@ -3335,7 +3335,7 @@ readJoy1: { // Sprite Data SPRITES: .byte $96, $36, 2, $c, $96, $37, 2, $14, $9e, $38, 2, $c, $9e, $39, 2, $14, $96, $70, 0, $48, $96, $71, 0, $50, $9e, $72, 1, $48, $9e, $73, 1, $50 // Color Palette - PALETTE: .byte $f, $2d, 8, $18, $f, 6, $15, $36, $f, $39, $4a, $5b, $f, $3d, $4e, $5f, $11, $f, $30, 8, $11, $f, $18, 8, $11, $30, $37, $1a, $f, $f, $f, $f + PALETTE: .byte $11, $2d, 8, $18, $11, 6, $15, $36, $11, $39, $4a, $5b, $f, $3d, $4e, $5f, $11, $f, $30, 8, $11, $f, $18, 8, $11, $30, $37, $1a, $f, $f, $f, $f .segment Tiles TILES: .import binary "smb1_chr.bin" @@ -4380,7 +4380,7 @@ readJoy1: { // Sprite Data SPRITES: .byte $96, $36, 2, $c, $96, $37, 2, $14, $9e, $38, 2, $c, $9e, $39, 2, $14, $96, $70, 0, $48, $96, $71, 0, $50, $9e, $72, 1, $48, $9e, $73, 1, $50 // Color Palette - PALETTE: .byte $f, $2d, 8, $18, $f, 6, $15, $36, $f, $39, $4a, $5b, $f, $3d, $4e, $5f, $11, $f, $30, 8, $11, $f, $18, 8, $11, $30, $37, $1a, $f, $f, $f, $f + PALETTE: .byte $11, $2d, 8, $18, $11, 6, $15, $36, $11, $39, $4a, $5b, $f, $3d, $4e, $5f, $11, $f, $30, 8, $11, $f, $18, 8, $11, $30, $37, $1a, $f, $f, $f, $f .segment Tiles TILES: .import binary "smb1_chr.bin" @@ -4576,7 +4576,7 @@ FINAL SYMBOL TABLE (const byte) OFFSET_STRUCT_RICOH_2C02_PPUSCROLL = (byte) 5 (const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS = (byte) 2 (const byte) OFFSET_STRUCT_SPRITEDATA_X = (byte) 3 -(const byte*) PALETTE[(number) $20] = { (byte) $f, (byte) $2d, (byte) 8, (byte) $18, (byte) $f, (byte) 6, (byte) $15, (byte) $36, (byte) $f, (byte) $39, (byte) $4a, (byte) $5b, (byte) $f, (byte) $3d, (byte) $4e, (byte) $5f, (byte) $11, (byte) $f, (byte) $30, (byte) 8, (byte) $11, (byte) $f, (byte) $18, (byte) 8, (byte) $11, (byte) $30, (byte) $37, (byte) $1a, (byte) $f, (byte) $f, (byte) $f, (byte) $f } +(const byte*) PALETTE[(number) $20] = { (byte) $11, (byte) $2d, (byte) 8, (byte) $18, (byte) $11, (byte) 6, (byte) $15, (byte) $36, (byte) $11, (byte) $39, (byte) $4a, (byte) $5b, (byte) $f, (byte) $3d, (byte) $4e, (byte) $5f, (byte) $11, (byte) $f, (byte) $30, (byte) 8, (byte) $11, (byte) $f, (byte) $18, (byte) 8, (byte) $11, (byte) $30, (byte) $37, (byte) $1a, (byte) $f, (byte) $f, (byte) $f, (byte) $f } (const struct RICOH_2C02*) PPU = (struct RICOH_2C02*) 8192 (const nomodify byte*) PPU_ATTRIBUTE_TABLE_0 = (byte*) 9152 (const nomodify byte*) PPU_NAME_TABLE_0 = (byte*) 8192 @@ -5606,7 +5606,7 @@ readJoy1: { // Sprite Data SPRITES: .byte $96, $36, 2, $c, $96, $37, 2, $14, $9e, $38, 2, $c, $9e, $39, 2, $14, $96, $70, 0, $48, $96, $71, 0, $50, $9e, $72, 1, $48, $9e, $73, 1, $50 // Color Palette - PALETTE: .byte $f, $2d, 8, $18, $f, 6, $15, $36, $f, $39, $4a, $5b, $f, $3d, $4e, $5f, $11, $f, $30, 8, $11, $f, $18, 8, $11, $30, $37, $1a, $f, $f, $f, $f + PALETTE: .byte $11, $2d, 8, $18, $11, 6, $15, $36, $11, $39, $4a, $5b, $f, $3d, $4e, $5f, $11, $f, $30, 8, $11, $f, $18, 8, $11, $30, $37, $1a, $f, $f, $f, $f .segment Tiles TILES: .import binary "smb1_chr.bin" diff --git a/src/test/ref/examples/nes/nes-demo.sym b/src/test/ref/examples/nes/nes-demo.sym index ef64da553..f772543f6 100644 --- a/src/test/ref/examples/nes/nes-demo.sym +++ b/src/test/ref/examples/nes/nes-demo.sym @@ -20,7 +20,7 @@ (const byte) OFFSET_STRUCT_RICOH_2C02_PPUSCROLL = (byte) 5 (const byte) OFFSET_STRUCT_RICOH_2C02_PPUSTATUS = (byte) 2 (const byte) OFFSET_STRUCT_SPRITEDATA_X = (byte) 3 -(const byte*) PALETTE[(number) $20] = { (byte) $f, (byte) $2d, (byte) 8, (byte) $18, (byte) $f, (byte) 6, (byte) $15, (byte) $36, (byte) $f, (byte) $39, (byte) $4a, (byte) $5b, (byte) $f, (byte) $3d, (byte) $4e, (byte) $5f, (byte) $11, (byte) $f, (byte) $30, (byte) 8, (byte) $11, (byte) $f, (byte) $18, (byte) 8, (byte) $11, (byte) $30, (byte) $37, (byte) $1a, (byte) $f, (byte) $f, (byte) $f, (byte) $f } +(const byte*) PALETTE[(number) $20] = { (byte) $11, (byte) $2d, (byte) 8, (byte) $18, (byte) $11, (byte) 6, (byte) $15, (byte) $36, (byte) $11, (byte) $39, (byte) $4a, (byte) $5b, (byte) $f, (byte) $3d, (byte) $4e, (byte) $5f, (byte) $11, (byte) $f, (byte) $30, (byte) 8, (byte) $11, (byte) $f, (byte) $18, (byte) 8, (byte) $11, (byte) $30, (byte) $37, (byte) $1a, (byte) $f, (byte) $f, (byte) $f, (byte) $f } (const struct RICOH_2C02*) PPU = (struct RICOH_2C02*) 8192 (const nomodify byte*) PPU_ATTRIBUTE_TABLE_0 = (byte*) 9152 (const nomodify byte*) PPU_NAME_TABLE_0 = (byte*) 8192 diff --git a/src/test/ref/minus-precedence-problem.asm b/src/test/ref/minus-precedence-problem.asm new file mode 100644 index 000000000..28ae7469c --- /dev/null +++ b/src/test/ref/minus-precedence-problem.asm @@ -0,0 +1,12 @@ +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .const matrixSize = 8 + .const matrixSizeMask = $ff-(matrixSize-1) +main: { + // *((unsigned char *)0x400) = matrixSizeMask + lda #matrixSizeMask + sta $400 + // } + rts +} diff --git a/src/test/ref/minus-precedence-problem.cfg b/src/test/ref/minus-precedence-problem.cfg new file mode 100644 index 000000000..224c5c546 --- /dev/null +++ b/src/test/ref/minus-precedence-problem.cfg @@ -0,0 +1,17 @@ +@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] *((byte*) 1024) ← (const nomodify byte) matrixSizeMask + to:main::@return +main::@return: scope:[main] from main + [5] return + to:@return diff --git a/src/test/ref/minus-precedence-problem.log b/src/test/ref/minus-precedence-problem.log new file mode 100644 index 000000000..32b6b3c73 --- /dev/null +++ b/src/test/ref/minus-precedence-problem.log @@ -0,0 +1,222 @@ + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@1 + +(void()) main() +main: scope:[main] from @1 + *((byte*)(number) $400) ← (const nomodify byte) matrixSizeMask + 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 +(void()) main() +(label) main::@return +(const nomodify byte) matrixSize = (byte) 8 +(const nomodify byte) matrixSizeMask = (byte)(number) $ff-(const nomodify byte) matrixSize-(number) 1 + +Adding number conversion cast (unumber) 1 in +Successful SSA optimization PassNAddNumberTypeConversions +Adding number conversion cast (unumber) $ff in +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant integer cast $ff +Simplifying constant integer cast 1 +Simplifying constant pointer cast (byte*) 1024 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) $ff +Finalized unsigned number type (byte) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simplifying constant integer cast (byte) $ff-(const nomodify byte) matrixSize-(byte) 1 +Successful SSA optimization PassNCastSimplification +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] *((byte*) 1024) ← (const nomodify byte) matrixSizeMask + to:main::@return +main::@return: scope:[main] from main + [5] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(void()) main() + +Initial phi equivalence classes +Complete equivalence classes + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .const matrixSize = 8 + .const matrixSizeMask = $ff-(matrixSize-1) + // @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: { + // [4] *((byte*) 1024) ← (const nomodify byte) matrixSizeMask -- _deref_pbuc1=vbuc2 + lda #matrixSizeMask + sta $400 + jmp __breturn + // main::@return + __breturn: + // [5] return + rts +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] *((byte*) 1024) ← (const nomodify byte) matrixSizeMask [ ] ( main:2 [ ] { } ) always clobbers reg byte a + +REGISTER UPLIFT SCOPES +Uplift Scope [main] +Uplift Scope [] + +Uplifting [main] best 27 combination +Uplifting [] best 27 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .const matrixSize = 8 + .const matrixSizeMask = $ff-(matrixSize-1) + // @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: { + // [4] *((byte*) 1024) ← (const nomodify byte) matrixSizeMask -- _deref_pbuc1=vbuc2 + lda #matrixSizeMask + sta $400 + jmp __breturn + // main::@return + __breturn: + // [5] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __bend +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __b1_from___bbegin: +Removing instruction __b1: +Removing instruction __bend_from___b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __bbegin: +Removing instruction __bend: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(void()) main() +(label) main::@return +(const nomodify byte) matrixSize = (byte) 8 +(const nomodify byte) matrixSizeMask = (byte) $ff-(const nomodify byte) matrixSize-(byte) 1 + + + +FINAL ASSEMBLER +Score: 12 + + // File Comments + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .const matrixSize = 8 + .const matrixSizeMask = $ff-(matrixSize-1) + // @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: { + // *((unsigned char *)0x400) = matrixSizeMask + // [4] *((byte*) 1024) ← (const nomodify byte) matrixSizeMask -- _deref_pbuc1=vbuc2 + lda #matrixSizeMask + sta $400 + // main::@return + // } + // [5] return + rts +} + // File Data + diff --git a/src/test/ref/minus-precedence-problem.sym b/src/test/ref/minus-precedence-problem.sym new file mode 100644 index 000000000..94f2b6ad4 --- /dev/null +++ b/src/test/ref/minus-precedence-problem.sym @@ -0,0 +1,8 @@ +(label) @1 +(label) @begin +(label) @end +(void()) main() +(label) main::@return +(const nomodify byte) matrixSize = (byte) 8 +(const nomodify byte) matrixSizeMask = (byte) $ff-(const nomodify byte) matrixSize-(byte) 1 + diff --git a/src/test/ref/problem-ma-var-overwrite.asm b/src/test/ref/problem-ma-var-overwrite.asm new file mode 100644 index 000000000..f0fcd58e4 --- /dev/null +++ b/src/test/ref/problem-ma-var-overwrite.asm @@ -0,0 +1,61 @@ +// Demonstrates that a local __ma variable overwrites a parameter§ +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + .label h1 = 2 +__bbegin: + // h1 + lda #<0 + sta.z h1 + sta.z h1+1 + jsr main + rts +main: { + // test(h1, 0xD800, 0xC000) + lda.z h1 + sta.z test.videoMem + lda.z h1+1 + sta.z test.videoMem+1 + jsr test + // } + rts +} +// This must be volatile because is used in an interrupt routine... +// test(byte* zp(4) videoMem) +test: { + .label colorMem = $d800 + .label other = $c000 + .label dst = 4 + .label __1 = 4 + .label diff = 4 + .label videoMem = 4 + // dst + lda #<0 + sta.z dst + sta.z dst+1 + // colorMem - videoMem + sec + lda #colorMem + sbc.z diff+1 + sta.z diff+1 + // other + ((unsigned int)diff) + clc + lda.z __1 + adc #other + sta.z __1+1 + // dst = other + ((unsigned int)diff) + // dst[0] = 1 + lda #1 + ldy #0 + sta (dst),y + // asm + sta (dst),y + // } + rts +} diff --git a/src/test/ref/problem-ma-var-overwrite.cfg b/src/test/ref/problem-ma-var-overwrite.cfg new file mode 100644 index 000000000..8e91fa3fe --- /dev/null +++ b/src/test/ref/problem-ma-var-overwrite.cfg @@ -0,0 +1,31 @@ +@begin: scope:[] from + [0] (volatile byte*) h1 ← (byte*) 0 + 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] (byte*) test::videoMem#0 ← (volatile byte*) h1 + [5] call test + to:main::@return +main::@return: scope:[main] from main + [6] return + to:@return + +(void()) test((byte*) test::videoMem , (byte*) test::colorMem , (byte*) test::other) +test: scope:[test] from main + [7] (byte*) test::dst ← (byte*) 0 + [8] (word) test::diff#1 ← (const byte*) test::colorMem#0 - (byte*) test::videoMem#0 + [9] (byte*~) test::$1 ← (const byte*) test::other#0 + (word)(byte*)(word) test::diff#1 + [10] (byte*) test::dst ← (byte*~) test::$1 + [11] *((byte*) test::dst) ← (byte) 1 + asm { ldy#0 lda#1 sta(dst),y } + to:test::@return +test::@return: scope:[test] from test + [13] return + to:@return diff --git a/src/test/ref/problem-ma-var-overwrite.log b/src/test/ref/problem-ma-var-overwrite.log new file mode 100644 index 000000000..e1397776a --- /dev/null +++ b/src/test/ref/problem-ma-var-overwrite.log @@ -0,0 +1,536 @@ + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (volatile byte*) h1 ← (byte*) 0 + to:@1 + +(void()) test((byte*) test::videoMem , (byte*) test::colorMem , (byte*) test::other) +test: scope:[test] from main + (byte*) test::other#1 ← phi( main/(byte*) test::other#0 ) + (byte*) test::videoMem#1 ← phi( main/(byte*) test::videoMem#0 ) + (byte*) test::colorMem#1 ← phi( main/(byte*) test::colorMem#0 ) + (byte*) test::diff#0 ← (byte*) 0 + (byte*) test::dst ← (byte*) 0 + (word~) test::$0 ← (byte*) test::colorMem#1 - (byte*) test::videoMem#1 + (byte*) test::diff#1 ← ((byte*)) (word~) test::$0 + (word~) test::$2 ← (word)(byte*) test::diff#1 + (byte*~) test::$1 ← (byte*) test::other#1 + (word~) test::$2 + (byte*) test::dst ← (byte*~) test::$1 + *((byte*) test::dst + (number) 0) ← (number) 1 + asm { ldy#0 lda#1 sta(dst),y } + to:test::@return +test::@return: scope:[test] from test + return + to:@return + +(void()) main() +main: scope:[main] from @1 + (byte*) test::videoMem#0 ← (volatile byte*) h1 + (byte*) test::colorMem#0 ← (byte*)(number) $d800 + (byte*) test::other#0 ← (byte*)(number) $c000 + call test + to:main::@1 +main::@1: scope:[main] from main + to:main::@return +main::@return: scope:[main] from main::@1 + 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 +(volatile byte*) h1 loadstore +(void()) main() +(label) main::@1 +(label) main::@return +(void()) test((byte*) test::videoMem , (byte*) test::colorMem , (byte*) test::other) +(word~) test::$0 +(byte*~) test::$1 +(word~) test::$2 +(label) test::@return +(byte*) test::colorMem +(byte*) test::colorMem#0 +(byte*) test::colorMem#1 +(byte*) test::diff +(byte*) test::diff#0 +(byte*) test::diff#1 +(byte*) test::dst loadstore +(byte*) test::other +(byte*) test::other#0 +(byte*) test::other#1 +(byte*) test::videoMem +(byte*) test::videoMem#0 +(byte*) test::videoMem#1 + +Adding number conversion cast (unumber) 1 in *((byte*) test::dst + (number) 0) ← (number) 1 +Adding number conversion cast (unumber) 0 in *((byte*) test::dst + (number) 0) ← ((unumber)) (number) 1 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (byte*) test::diff#1 ← (byte*)(word~) test::$0 +Inlining cast *((byte*) test::dst + (unumber)(number) 0) ← (unumber)(number) 1 +Successful SSA optimization Pass2InlineCast +Simplifying constant integer cast 1 +Simplifying constant integer cast 0 +Simplifying constant pointer cast (byte*) 55296 +Simplifying constant pointer cast (byte*) 49152 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 1 +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias candidate removed (volatile)test::dst = test::$1 +Identical Phi Values (byte*) test::colorMem#1 (byte*) test::colorMem#0 +Identical Phi Values (byte*) test::videoMem#1 (byte*) test::videoMem#0 +Identical Phi Values (byte*) test::other#1 (byte*) test::other#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Constant (const byte*) test::diff#0 = (byte*) 0 +Constant (const byte*) test::colorMem#0 = (byte*) 55296 +Constant (const byte*) test::other#0 = (byte*) 49152 +Successful SSA optimization Pass2ConstantIdentification +Simplifying expression containing zero test::dst in [9] *((byte*) test::dst + (byte) 0) ← (byte) 1 +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused constant (const byte*) test::diff#0 +Successful SSA optimization PassNEliminateUnusedVars +Alias candidate removed (volatile)test::dst = test::$1 +Inlining Noop Cast [3] (byte*) test::diff#1 ← (byte*)(word~) test::$0 keeping test::diff#1 +Successful SSA optimization Pass2NopCastInlining +Inlining Noop Cast [4] (word~) test::$2 ← (word)(byte*)(word) test::diff#1 keeping (byte*)test::diff#1 +Successful SSA optimization Pass2NopCastInlining +Alias candidate removed (volatile)test::dst = test::$1 +Alias candidate removed (volatile)test::dst = test::$1 +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main::@1 +CALL GRAPH +Calls in [] to main:2 +Calls in [main] to test:6 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Culled Empty Block (label) @2 +Culled Empty Block (label) main::@1 +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] (volatile byte*) h1 ← (byte*) 0 + 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] (byte*) test::videoMem#0 ← (volatile byte*) h1 + [5] call test + to:main::@return +main::@return: scope:[main] from main + [6] return + to:@return + +(void()) test((byte*) test::videoMem , (byte*) test::colorMem , (byte*) test::other) +test: scope:[test] from main + [7] (byte*) test::dst ← (byte*) 0 + [8] (word) test::diff#1 ← (const byte*) test::colorMem#0 - (byte*) test::videoMem#0 + [9] (byte*~) test::$1 ← (const byte*) test::other#0 + (word)(byte*)(word) test::diff#1 + [10] (byte*) test::dst ← (byte*~) test::$1 + [11] *((byte*) test::dst) ← (byte) 1 + asm { ldy#0 lda#1 sta(dst),y } + to:test::@return +test::@return: scope:[test] from test + [13] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(volatile byte*) h1 loadstore 6.5 +(void()) main() +(void()) test((byte*) test::videoMem , (byte*) test::colorMem , (byte*) test::other) +(byte*~) test::$1 202.0 +(byte*) test::colorMem +(byte*) test::diff +(word) test::diff#1 101.0 +(byte*) test::dst loadstore 151.5 +(byte*) test::other +(byte*) test::videoMem +(byte*) test::videoMem#0 56.0 + +Initial phi equivalence classes +Added variable h1 to live range equivalence class [ h1 ] +Added variable test::videoMem#0 to live range equivalence class [ test::videoMem#0 ] +Added variable test::dst to live range equivalence class [ test::dst ] +Added variable test::diff#1 to live range equivalence class [ test::diff#1 ] +Added variable test::$1 to live range equivalence class [ test::$1 ] +Complete equivalence classes +[ h1 ] +[ test::videoMem#0 ] +[ test::dst ] +[ test::diff#1 ] +[ test::$1 ] +Allocated zp[2]:2 [ h1 ] +Allocated zp[2]:4 [ test::videoMem#0 ] +Allocated zp[2]:6 [ test::dst ] +Allocated zp[2]:8 [ test::diff#1 ] +Allocated zp[2]:10 [ test::$1 ] + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Demonstrates that a local __ma variable overwrites a parameter§ + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label h1 = 2 + // @begin +__bbegin: + // [0] (volatile byte*) h1 ← (byte*) 0 -- pbuz1=pbuc1 + lda #<0 + sta.z h1 + lda #>0 + sta.z h1+1 + // [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: { + // [4] (byte*) test::videoMem#0 ← (volatile byte*) h1 -- pbuz1=pbuz2 + lda.z h1 + sta.z test.videoMem + lda.z h1+1 + sta.z test.videoMem+1 + // [5] call test + jsr test + jmp __breturn + // main::@return + __breturn: + // [6] return + rts +} + // test +// This must be volatile because is used in an interrupt routine... +// test(byte* zp(4) videoMem) +test: { + .label colorMem = $d800 + .label other = $c000 + .label dst = 6 + .label __1 = $a + .label diff = 8 + .label videoMem = 4 + // [7] (byte*) test::dst ← (byte*) 0 -- pbuz1=pbuc1 + lda #<0 + sta.z dst + lda #>0 + sta.z dst+1 + // [8] (word) test::diff#1 ← (const byte*) test::colorMem#0 - (byte*) test::videoMem#0 -- vwuz1=pbuc1_minus_pbuz2 + sec + lda #colorMem + sbc.z videoMem+1 + sta.z diff+1 + // [9] (byte*~) test::$1 ← (const byte*) test::other#0 + (word)(byte*)(word) test::diff#1 -- pbuz1=pbuc1_plus_vwuz2 + lda.z diff + clc + adc #other + sta.z __1+1 + // [10] (byte*) test::dst ← (byte*~) test::$1 -- pbuz1=pbuz2 + lda.z __1 + sta.z dst + lda.z __1+1 + sta.z dst+1 + // [11] *((byte*) test::dst) ← (byte) 1 -- _deref_pbuz1=vbuc1 + lda #1 + ldy #0 + sta (dst),y + // asm { ldy#0 lda#1 sta(dst),y } + ldy #0 + lda #1 + sta (dst),y + jmp __breturn + // test::@return + __breturn: + // [13] return + rts +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] (volatile byte*) h1 ← (byte*) 0 [ h1 ] ( [ h1 ] { } ) always clobbers reg byte a +Statement [4] (byte*) test::videoMem#0 ← (volatile byte*) h1 [ test::videoMem#0 ] ( main:2 [ test::videoMem#0 ] { { test::videoMem#0 = h1 } } ) always clobbers reg byte a +Statement [7] (byte*) test::dst ← (byte*) 0 [ test::videoMem#0 ] ( main:2::test:5 [ test::videoMem#0 ] { { test::videoMem#0 = h1 } } ) always clobbers reg byte a +Statement [8] (word) test::diff#1 ← (const byte*) test::colorMem#0 - (byte*) test::videoMem#0 [ test::diff#1 ] ( main:2::test:5 [ test::diff#1 ] { { test::videoMem#0 = h1 } } ) always clobbers reg byte a +Statement [9] (byte*~) test::$1 ← (const byte*) test::other#0 + (word)(byte*)(word) test::diff#1 [ test::$1 ] ( main:2::test:5 [ test::$1 ] { { test::videoMem#0 = h1 } } ) always clobbers reg byte a +Statement [10] (byte*) test::dst ← (byte*~) test::$1 [ test::dst ] ( main:2::test:5 [ test::dst ] { { test::videoMem#0 = h1 } } ) always clobbers reg byte a +Statement [11] *((byte*) test::dst) ← (byte) 1 [ test::dst ] ( main:2::test:5 [ test::dst ] { { test::videoMem#0 = h1 } } ) always clobbers reg byte a reg byte y +Statement asm { ldy#0 lda#1 sta(dst),y } always clobbers reg byte a reg byte y +Potential registers zp[2]:2 [ h1 ] : zp[2]:2 , +Potential registers zp[2]:4 [ test::videoMem#0 ] : zp[2]:4 , +Potential registers zp[2]:6 [ test::dst ] : zp[2]:6 , +Potential registers zp[2]:8 [ test::diff#1 ] : zp[2]:8 , +Potential registers zp[2]:10 [ test::$1 ] : zp[2]:10 , + +REGISTER UPLIFT SCOPES +Uplift Scope [test] 202: zp[2]:10 [ test::$1 ] 151.5: zp[2]:6 [ test::dst ] 101: zp[2]:8 [ test::diff#1 ] 56: zp[2]:4 [ test::videoMem#0 ] +Uplift Scope [] 6.5: zp[2]:2 [ h1 ] +Uplift Scope [main] + +Uplifting [test] best 136 combination zp[2]:10 [ test::$1 ] zp[2]:6 [ test::dst ] zp[2]:8 [ test::diff#1 ] zp[2]:4 [ test::videoMem#0 ] +Uplifting [] best 136 combination zp[2]:2 [ h1 ] +Uplifting [main] best 136 combination +Coalescing zero page register [ zp[2]:4 [ test::videoMem#0 ] ] with [ zp[2]:8 [ test::diff#1 ] ] - score: 1 +Coalescing zero page register [ zp[2]:6 [ test::dst ] ] with [ zp[2]:10 [ test::$1 ] ] - score: 1 +Coalescing zero page register [ zp[2]:4 [ test::videoMem#0 test::diff#1 ] ] with [ zp[2]:6 [ test::dst test::$1 ] ] - score: 1 + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Demonstrates that a local __ma variable overwrites a parameter§ + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label h1 = 2 + // @begin +__bbegin: + // [0] (volatile byte*) h1 ← (byte*) 0 -- pbuz1=pbuc1 + lda #<0 + sta.z h1 + lda #>0 + sta.z h1+1 + // [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: { + // [4] (byte*) test::videoMem#0 ← (volatile byte*) h1 -- pbuz1=pbuz2 + lda.z h1 + sta.z test.videoMem + lda.z h1+1 + sta.z test.videoMem+1 + // [5] call test + jsr test + jmp __breturn + // main::@return + __breturn: + // [6] return + rts +} + // test +// This must be volatile because is used in an interrupt routine... +// test(byte* zp(4) videoMem) +test: { + .label colorMem = $d800 + .label other = $c000 + .label dst = 4 + .label __1 = 4 + .label diff = 4 + .label videoMem = 4 + // [7] (byte*) test::dst ← (byte*) 0 -- pbuz1=pbuc1 + lda #<0 + sta.z dst + lda #>0 + sta.z dst+1 + // [8] (word) test::diff#1 ← (const byte*) test::colorMem#0 - (byte*) test::videoMem#0 -- vwuz1=pbuc1_minus_pbuz1 + sec + lda #colorMem + sbc.z diff+1 + sta.z diff+1 + // [9] (byte*~) test::$1 ← (const byte*) test::other#0 + (word)(byte*)(word) test::diff#1 -- pbuz1=pbuc1_plus_vwuz1 + clc + lda.z __1 + adc #other + sta.z __1+1 + // [10] (byte*) test::dst ← (byte*~) test::$1 + // [11] *((byte*) test::dst) ← (byte) 1 -- _deref_pbuz1=vbuc1 + lda #1 + ldy #0 + sta (dst),y + // asm { ldy#0 lda#1 sta(dst),y } + ldy #0 + lda #1 + sta (dst),y + jmp __breturn + // test::@return + __breturn: + // [13] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __bend +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction lda #>0 +Removing instruction lda #>0 +Removing instruction ldy #0 +Removing instruction lda #1 +Succesful ASM optimization Pass5UnnecesaryLoadElimination +Removing instruction __b1_from___bbegin: +Removing instruction __bend_from___b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __b1: +Removing instruction __bend: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Adding RTS to root block +Succesful ASM optimization Pass5AddMainRts + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(volatile byte*) h1 loadstore zp[2]:2 6.5 +(void()) main() +(label) main::@return +(void()) test((byte*) test::videoMem , (byte*) test::colorMem , (byte*) test::other) +(byte*~) test::$1 zp[2]:4 202.0 +(label) test::@return +(byte*) test::colorMem +(const byte*) test::colorMem#0 colorMem = (byte*) 55296 +(byte*) test::diff +(word) test::diff#1 diff zp[2]:4 101.0 +(byte*) test::dst loadstore zp[2]:4 151.5 +(byte*) test::other +(const byte*) test::other#0 other = (byte*) 49152 +(byte*) test::videoMem +(byte*) test::videoMem#0 videoMem zp[2]:4 56.0 + +zp[2]:2 [ h1 ] +zp[2]:4 [ test::videoMem#0 test::diff#1 test::dst test::$1 ] + + +FINAL ASSEMBLER +Score: 110 + + // File Comments +// Demonstrates that a local __ma variable overwrites a parameter§ + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label h1 = 2 + // @begin +__bbegin: + // h1 + // [0] (volatile byte*) h1 ← (byte*) 0 -- pbuz1=pbuc1 + lda #<0 + sta.z h1 + sta.z h1+1 + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + jsr main + rts + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + // test(h1, 0xD800, 0xC000) + // [4] (byte*) test::videoMem#0 ← (volatile byte*) h1 -- pbuz1=pbuz2 + lda.z h1 + sta.z test.videoMem + lda.z h1+1 + sta.z test.videoMem+1 + // [5] call test + jsr test + // main::@return + // } + // [6] return + rts +} + // test +// This must be volatile because is used in an interrupt routine... +// test(byte* zp(4) videoMem) +test: { + .label colorMem = $d800 + .label other = $c000 + .label dst = 4 + .label __1 = 4 + .label diff = 4 + .label videoMem = 4 + // dst + // [7] (byte*) test::dst ← (byte*) 0 -- pbuz1=pbuc1 + lda #<0 + sta.z dst + sta.z dst+1 + // colorMem - videoMem + // [8] (word) test::diff#1 ← (const byte*) test::colorMem#0 - (byte*) test::videoMem#0 -- vwuz1=pbuc1_minus_pbuz1 + sec + lda #colorMem + sbc.z diff+1 + sta.z diff+1 + // other + ((unsigned int)diff) + // [9] (byte*~) test::$1 ← (const byte*) test::other#0 + (word)(byte*)(word) test::diff#1 -- pbuz1=pbuc1_plus_vwuz1 + clc + lda.z __1 + adc #other + sta.z __1+1 + // dst = other + ((unsigned int)diff) + // [10] (byte*) test::dst ← (byte*~) test::$1 + // dst[0] = 1 + // [11] *((byte*) test::dst) ← (byte) 1 -- _deref_pbuz1=vbuc1 + lda #1 + ldy #0 + sta (dst),y + // asm + // asm { ldy#0 lda#1 sta(dst),y } + sta (dst),y + // test::@return + // } + // [13] return + rts +} + // File Data + diff --git a/src/test/ref/problem-ma-var-overwrite.sym b/src/test/ref/problem-ma-var-overwrite.sym new file mode 100644 index 000000000..8a81731bd --- /dev/null +++ b/src/test/ref/problem-ma-var-overwrite.sym @@ -0,0 +1,21 @@ +(label) @1 +(label) @begin +(label) @end +(volatile byte*) h1 loadstore zp[2]:2 6.5 +(void()) main() +(label) main::@return +(void()) test((byte*) test::videoMem , (byte*) test::colorMem , (byte*) test::other) +(byte*~) test::$1 zp[2]:4 202.0 +(label) test::@return +(byte*) test::colorMem +(const byte*) test::colorMem#0 colorMem = (byte*) 55296 +(byte*) test::diff +(word) test::diff#1 diff zp[2]:4 101.0 +(byte*) test::dst loadstore zp[2]:4 151.5 +(byte*) test::other +(const byte*) test::other#0 other = (byte*) 49152 +(byte*) test::videoMem +(byte*) test::videoMem#0 videoMem zp[2]:4 56.0 + +zp[2]:2 [ h1 ] +zp[2]:4 [ test::videoMem#0 test::diff#1 test::dst test::$1 ]