mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-11-26 12:49:21 +00:00
Fixed problem with incorrect parenthesising of non-associative binary operators in constant expressions. Closes #450
This commit is contained in:
parent
67dedcc299
commit
a63adec0f0
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
7
src/test/kc/minus-precedence-problem.c
Normal file
7
src/test/kc/minus-precedence-problem.c
Normal file
@ -0,0 +1,7 @@
|
||||
const unsigned char matrixSize = 8;
|
||||
const unsigned char matrixSizeMask = 255 - (matrixSize - 1);
|
||||
|
||||
void main(void)
|
||||
{
|
||||
*((unsigned char *)0x400) = matrixSizeMask;
|
||||
}
|
25
src/test/kc/problem-ma-var-overwrite.c
Normal file
25
src/test/kc/problem-ma-var-overwrite.c
Normal file
@ -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);
|
||||
}
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
12
src/test/ref/minus-precedence-problem.asm
Normal file
12
src/test/ref/minus-precedence-problem.asm
Normal file
@ -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
|
||||
}
|
17
src/test/ref/minus-precedence-problem.cfg
Normal file
17
src/test/ref/minus-precedence-problem.cfg
Normal file
@ -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
|
222
src/test/ref/minus-precedence-problem.log
Normal file
222
src/test/ref/minus-precedence-problem.log
Normal file
@ -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
|
||||
|
8
src/test/ref/minus-precedence-problem.sym
Normal file
8
src/test/ref/minus-precedence-problem.sym
Normal file
@ -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
|
||||
|
61
src/test/ref/problem-ma-var-overwrite.asm
Normal file
61
src/test/ref/problem-ma-var-overwrite.asm
Normal file
@ -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
|
||||
sta.z diff
|
||||
lda #>colorMem
|
||||
sbc.z diff+1
|
||||
sta.z diff+1
|
||||
// other + ((unsigned int)diff)
|
||||
clc
|
||||
lda.z __1
|
||||
adc #<other
|
||||
sta.z __1
|
||||
lda.z __1+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
|
||||
}
|
31
src/test/ref/problem-ma-var-overwrite.cfg
Normal file
31
src/test/ref/problem-ma-var-overwrite.cfg
Normal file
@ -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
|
536
src/test/ref/problem-ma-var-overwrite.log
Normal file
536
src/test/ref/problem-ma-var-overwrite.log
Normal file
@ -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
|
||||
sta.z diff
|
||||
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
|
||||
lda.z diff+1
|
||||
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
|
||||
sta.z diff
|
||||
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
|
||||
lda.z __1+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
|
||||
sta.z diff
|
||||
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
|
||||
lda.z __1+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
|
||||
|
21
src/test/ref/problem-ma-var-overwrite.sym
Normal file
21
src/test/ref/problem-ma-var-overwrite.sym
Normal file
@ -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 ]
|
Loading…
Reference in New Issue
Block a user