mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-01-13 18:30:21 +00:00
Fixed Wrong size of padding for non-byte arrays. Closes #497
This commit is contained in:
parent
dcb68b8c1a
commit
9227a3a857
@ -313,7 +313,7 @@ public class AsmChunk {
|
||||
line instanceof AsmLabelDecl ||
|
||||
line instanceof AsmConstant ||
|
||||
line instanceof AsmDataNumeric ||
|
||||
line instanceof AsmDataFill ||
|
||||
line instanceof AsmDataZeroFill ||
|
||||
line instanceof AsmDataString ||
|
||||
line instanceof AsmDataAlignment ||
|
||||
(line instanceof AsmInlineKickAsm && !line.getAsm().contains(".pc"));
|
||||
|
@ -42,24 +42,22 @@ public class AsmDataChunk {
|
||||
}
|
||||
}
|
||||
|
||||
/** A number of identical numerical data elements. */
|
||||
public static class AsmDataFilledElement implements AsmDataElement {
|
||||
/** A number of zero data elements. */
|
||||
public static class AsmDataZeroFilledElement implements AsmDataElement {
|
||||
|
||||
/** The ASM specifying how the total size in bytes - in ASM-format. */
|
||||
String totalSizeBytesAsm;
|
||||
/** The type of the element */
|
||||
AsmDataNumeric.Type type;
|
||||
/** The literal integer number of elements. */
|
||||
int numElements;
|
||||
/** The fill value. */
|
||||
String fillValue;
|
||||
/** The string encoding used in any char/string value */
|
||||
Set<StringEncoding> encoding;
|
||||
|
||||
AsmDataFilledElement(AsmDataNumeric.Type type, String totalSizeBytesAsm, int numElements, String fillValue, Set<StringEncoding> encoding) {
|
||||
AsmDataZeroFilledElement(AsmDataNumeric.Type type, String totalSizeBytesAsm, int numElements, Set<StringEncoding> encoding) {
|
||||
this.type = type;
|
||||
this.totalSizeBytesAsm = totalSizeBytesAsm;
|
||||
this.numElements = numElements;
|
||||
this.fillValue = fillValue;
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
@ -75,10 +73,6 @@ public class AsmDataChunk {
|
||||
return numElements;
|
||||
}
|
||||
|
||||
String getFillValue() {
|
||||
return fillValue;
|
||||
}
|
||||
|
||||
public Set<StringEncoding> getEncoding() {
|
||||
return encoding;
|
||||
}
|
||||
@ -145,8 +139,8 @@ public class AsmDataChunk {
|
||||
elements.add(new AsmDataNumericElement(type, value, encoding));
|
||||
}
|
||||
|
||||
public void addDataFilled(AsmDataNumeric.Type type, String totalSizeBytesAsm, int numElements, String fillValue, Set<StringEncoding> encoding) {
|
||||
elements.add(new AsmDataFilledElement(type, totalSizeBytesAsm, numElements, fillValue, encoding));
|
||||
public void addDataZeroFilled(AsmDataNumeric.Type type, String totalSizeBytesAsm, int numElements, Set<StringEncoding> encoding) {
|
||||
elements.add(new AsmDataZeroFilledElement(type, totalSizeBytesAsm, numElements, encoding));
|
||||
}
|
||||
|
||||
public void addDataString(String string, Set<StringEncoding> encoding) {
|
||||
@ -173,7 +167,7 @@ public class AsmDataChunk {
|
||||
AsmDataNumeric.Type currentNumericType = null;
|
||||
List<String> currentNumericElements = null;
|
||||
for(AsmDataChunk.AsmDataElement element : this.getElements()) {
|
||||
if(element instanceof AsmDataFilledElement || element instanceof AsmDataStringElement || element instanceof AsmDataKickAsmElement) {
|
||||
if(element instanceof AsmDataZeroFilledElement || element instanceof AsmDataStringElement || element instanceof AsmDataKickAsmElement) {
|
||||
if(currentNumericElements != null && currentNumericElements.size() > 0) {
|
||||
asm.addDataNumeric(label, currentNumericType, currentNumericElements);
|
||||
label = null; // Only output label once
|
||||
@ -181,10 +175,10 @@ public class AsmDataChunk {
|
||||
currentNumericType = null;
|
||||
}
|
||||
}
|
||||
if(element instanceof AsmDataFilledElement) {
|
||||
AsmDataFilledElement filledElement = (AsmDataFilledElement) element;
|
||||
if(element instanceof AsmDataZeroFilledElement) {
|
||||
AsmDataZeroFilledElement filledElement = (AsmDataZeroFilledElement) element;
|
||||
asm.ensureEncoding(filledElement.getEncoding());
|
||||
asm.addDataFilled(label, filledElement.getType(), filledElement.getTotalSizeBytesAsm(), filledElement.getNumElements(), filledElement.getFillValue());
|
||||
asm.addDataZeroFilled(label, filledElement.getType(), filledElement.getTotalSizeBytesAsm(), filledElement.getNumElements());
|
||||
label = null; // Only output label once
|
||||
} else if(element instanceof AsmDataKickAsmElement) {
|
||||
AsmDataKickAsmElement kickAsmElement = (AsmDataKickAsmElement) element;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package dk.camelot64.kickc.asm;
|
||||
|
||||
/** A labelled numeric data directive. */
|
||||
public class AsmDataFill implements AsmLine {
|
||||
/** A labelled zero-filled data directive. */
|
||||
public class AsmDataZeroFill implements AsmLine {
|
||||
|
||||
private String label;
|
||||
/** The calculation of the total number of bytes in ASM-format */
|
||||
@ -10,18 +10,14 @@ public class AsmDataFill implements AsmLine {
|
||||
private AsmDataNumeric.Type type;
|
||||
/** The number of elements*/
|
||||
private int numElements;
|
||||
/** The value to fill with in ASM-format */
|
||||
private String fillValue;
|
||||
|
||||
private int index;
|
||||
|
||||
|
||||
public AsmDataFill(String label, AsmDataNumeric.Type type, String totalByteSizeAsm, int numElements, String fillValue) {
|
||||
public AsmDataZeroFill(String label, AsmDataNumeric.Type type, String totalByteSizeAsm, int numElements) {
|
||||
this.label = label;
|
||||
this.type = type;
|
||||
this.totalByteSizeAsm = totalByteSizeAsm;
|
||||
this.numElements = numElements;
|
||||
this.fillValue = fillValue;
|
||||
}
|
||||
|
||||
public int getElementBytes() {
|
||||
@ -46,8 +42,7 @@ public class AsmDataFill implements AsmLine {
|
||||
}
|
||||
asm.append(".fill ");
|
||||
asm.append(totalByteSizeAsm);
|
||||
asm.append(", ");
|
||||
asm.append(fillValue);
|
||||
asm.append(", 0");
|
||||
return asm.toString();
|
||||
}
|
||||
|
@ -142,8 +142,8 @@ public class AsmProgram {
|
||||
* @param type The type of the data
|
||||
* @param numElements The size of data to fill
|
||||
*/
|
||||
public void addDataFilled(String label, AsmDataNumeric.Type type, String totalSizeBytesAsm, int numElements, String fillValue) {
|
||||
addLine(new AsmDataFill(label, type, totalSizeBytesAsm, numElements, fillValue));
|
||||
public void addDataZeroFilled(String label, AsmDataNumeric.Type type, String totalSizeBytesAsm, int numElements) {
|
||||
addLine(new AsmDataZeroFill(label, type, totalSizeBytesAsm, numElements));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -665,7 +665,7 @@ public class Pass4CodeGeneration {
|
||||
final ConstantRef structSize = SizeOfConstants.getSizeOfConstantVar(getScope(), typeStruct);
|
||||
String totalSizeBytesAsm = AsmFormat.getAsmConstant(program, structSize, 99, scopeRef);
|
||||
int totalSizeBytes = typeStruct.getSizeBytes();
|
||||
dataChunk.addDataFilled(AsmDataNumeric.Type.BYTE, totalSizeBytesAsm, totalSizeBytes, "0", null);
|
||||
dataChunk.addDataZeroFilled(AsmDataNumeric.Type.BYTE, totalSizeBytesAsm, totalSizeBytes, null);
|
||||
}
|
||||
} else if(valueType instanceof SymbolTypePointer && valueArraySpec != null) {
|
||||
SymbolTypePointer constTypeArray = (SymbolTypePointer) valueType;
|
||||
@ -684,17 +684,18 @@ public class Pass4CodeGeneration {
|
||||
int elementSizeBytes = elementType.getSizeBytes();
|
||||
String totalSizeBytesAsm;
|
||||
if(elementSizeBytes > 1) {
|
||||
// TODO: Use a SIZEOF constant for the element size ASM
|
||||
totalSizeBytesAsm = AsmFormat.getAsmConstant(program, new ConstantBinary(new ConstantInteger((long) elementSizeBytes, SymbolType.NUMBER), Operators.MULTIPLY, arraySize), 99, scopeRef);
|
||||
} else {
|
||||
totalSizeBytesAsm = AsmFormat.getAsmConstant(program, arraySize, 99, scopeRef);
|
||||
}
|
||||
if(elementType instanceof SymbolTypeIntegerFixed || elementType instanceof SymbolTypePointer) {
|
||||
// Use an ASM type in the fill that matches the element type
|
||||
dataChunk.addDataFilled(getNumericType(elementType), totalSizeBytesAsm, dataNumElements, "0", null);
|
||||
dataChunk.addDataZeroFilled(getNumericType(elementType), totalSizeBytesAsm, dataNumElements, null);
|
||||
} else {
|
||||
// Complex fill type - calculate byte size and use that
|
||||
int totalSizeBytes = elementSizeBytes * dataNumElements;
|
||||
dataChunk.addDataFilled(AsmDataNumeric.Type.BYTE, totalSizeBytesAsm, totalSizeBytes, "0", null);
|
||||
dataChunk.addDataZeroFilled(AsmDataNumeric.Type.BYTE, totalSizeBytesAsm, totalSizeBytes, null);
|
||||
}
|
||||
} else if(value instanceof ConstantArrayKickAsm) {
|
||||
ConstantArrayKickAsm kickAsm = (ConstantArrayKickAsm) value;
|
||||
@ -727,31 +728,34 @@ public class Pass4CodeGeneration {
|
||||
if(!(value instanceof ConstantArrayKickAsm)) {
|
||||
Integer declaredSize = getArrayDeclaredSize(valueArraySpec.getArraySize());
|
||||
if(declaredSize != null && declaredSize > dataNumElements) {
|
||||
int padding = declaredSize - dataNumElements;
|
||||
ConstantValue zeroValue = Initializers.createZeroValue(new Initializers.ValueTypeSpec(elementType, valueArraySpec), null);
|
||||
if(zeroValue instanceof ConstantInteger) {
|
||||
dataChunk.addDataFilled(getNumericType(elementType), AsmFormat.getAsmNumber(padding), padding, AsmFormat.getAsmConstant(program, zeroValue, 99, scopeRef), getEncoding(zeroValue));
|
||||
long paddingSize = declaredSize - dataNumElements;
|
||||
ConstantValue paddingSizeVal = new ConstantInteger(paddingSize);
|
||||
int elementSizeBytes = elementType.getSizeBytes();
|
||||
String paddingBytesAsm;
|
||||
if(elementSizeBytes > 1) {
|
||||
// TODO: Use a SIZEOF constant for the element size ASM - combine this with ConstantArrayFilled above
|
||||
paddingBytesAsm = AsmFormat.getAsmConstant(program, new ConstantBinary(new ConstantInteger((long) elementSizeBytes, SymbolType.NUMBER), Operators.MULTIPLY, paddingSizeVal), 99, scopeRef);
|
||||
} else {
|
||||
for(int i = 0; i < padding; i++) {
|
||||
paddingBytesAsm = AsmFormat.getAsmConstant(program, paddingSizeVal, 99, scopeRef);
|
||||
}
|
||||
ConstantValue zeroValue = Initializers.createZeroValue(new Initializers.ValueTypeSpec(elementType, null), null);
|
||||
if(zeroValue instanceof ConstantInteger | zeroValue instanceof ConstantPointer) {
|
||||
dataChunk.addDataZeroFilled(getNumericType(elementType), paddingBytesAsm, (int) paddingSize, getEncoding(zeroValue));
|
||||
} else {
|
||||
for(int i = 0; i < paddingSize; i++) {
|
||||
addChunkData(dataChunk, zeroValue, elementType, null, scopeRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(value instanceof ConstantString) {
|
||||
try {
|
||||
ConstantLiteral literal = value.calculateLiteral(getScope());
|
||||
if(literal instanceof ConstantString) {
|
||||
ConstantString stringValue = (ConstantString) value;
|
||||
// Ensure encoding is good
|
||||
String asmConstant = AsmFormat.getAsmConstant(program, literal, 99, scopeRef);
|
||||
dataChunk.addDataString(asmConstant, getEncoding(literal));
|
||||
if(((ConstantString) literal).isZeroTerminated()) {
|
||||
String asmConstant = AsmFormat.getAsmConstant(program, stringValue, 99, scopeRef);
|
||||
dataChunk.addDataString(asmConstant, getEncoding(stringValue));
|
||||
if(stringValue.isZeroTerminated()) {
|
||||
dataChunk.addDataNumeric(AsmDataNumeric.Type.BYTE, "0", null);
|
||||
}
|
||||
}
|
||||
} catch(ConstantNotLiteral e) {
|
||||
// can't calculate literal value, so it is not data - just return
|
||||
}
|
||||
} else if(SymbolType.BYTE.equals(valueType) || SymbolType.SBYTE.equals(valueType)) {
|
||||
dataChunk.addDataNumeric(AsmDataNumeric.Type.BYTE, AsmFormat.getAsmConstant(program, value, 99, scopeRef), getEncoding(value));
|
||||
} else if(SymbolType.WORD.equals(valueType) || SymbolType.SWORD.equals(valueType)) {
|
||||
|
@ -37,7 +37,7 @@ public class Pass5DoubleJumpElimination extends Pass5AsmOptimization {
|
||||
currentLabel = ((AsmLabel) line).getLabel();
|
||||
} else if(line instanceof AsmComment || line instanceof AsmConstant || line instanceof AsmLabelDecl) {
|
||||
// ignore
|
||||
} else if(line instanceof AsmBasicUpstart || line instanceof AsmDataNumeric || line instanceof AsmDataFill || line instanceof AsmDataString || line instanceof AsmDataAlignment || line instanceof AsmSetPc || line instanceof AsmInlineKickAsm|| line instanceof AsmSetEncoding|| line instanceof AsmSetCpu|| line instanceof AsmDataKickAsm|| line instanceof AsmSegmentDef|| line instanceof AsmSegment|| line instanceof AsmFile) {
|
||||
} else if(line instanceof AsmBasicUpstart || line instanceof AsmDataNumeric || line instanceof AsmDataZeroFill || line instanceof AsmDataString || line instanceof AsmDataAlignment || line instanceof AsmSetPc || line instanceof AsmInlineKickAsm|| line instanceof AsmSetEncoding|| line instanceof AsmSetCpu|| line instanceof AsmDataKickAsm|| line instanceof AsmSegmentDef|| line instanceof AsmSegment|| line instanceof AsmFile) {
|
||||
currentLabel = null;
|
||||
} else if(line instanceof AsmInstruction) {
|
||||
if(currentLabel != null) {
|
||||
|
@ -36,7 +36,7 @@ public class Pass5NextJumpElimination extends Pass5AsmOptimization {
|
||||
if(instruction.getAsmOpcode().isJump() && !instruction.getAsmOpcode().getMnemonic().equals("jsr")) {
|
||||
candidate = instruction;
|
||||
}
|
||||
} else if(line instanceof AsmDataString || line instanceof AsmDataNumeric || line instanceof AsmDataFill || line instanceof AsmInlineKickAsm ) {
|
||||
} else if(line instanceof AsmDataString || line instanceof AsmDataNumeric || line instanceof AsmDataZeroFill || line instanceof AsmInlineKickAsm ) {
|
||||
candidate = null;
|
||||
}
|
||||
}
|
||||
|
@ -1263,6 +1263,11 @@ public class TestPrograms {
|
||||
compileAndCompare("coalesce-assignment.c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArray16bitInit() throws IOException, URISyntaxException {
|
||||
compileAndCompare("array-16bit-init.c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArray16bitLookup() throws IOException, URISyntaxException {
|
||||
compileAndCompare("array-16bit-lookup.c");
|
||||
|
9
src/test/kc/array-16bit-init.c
Normal file
9
src/test/kc/array-16bit-init.c
Normal file
@ -0,0 +1,9 @@
|
||||
// Demonstrates wrong padding for non-byte arrays.
|
||||
// https://gitlab.com/camelot/kickc/-/issues/497
|
||||
|
||||
char* levelRowOff[31] = { 1, 2, 3 };
|
||||
|
||||
void main() {
|
||||
for(char c=0;c<sizeof(levelRowOff)/sizeof(char*); c++)
|
||||
levelRowOff[c] = 12345;
|
||||
}
|
29
src/test/ref/array-16bit-init.asm
Normal file
29
src/test/ref/array-16bit-init.asm
Normal file
@ -0,0 +1,29 @@
|
||||
// Demonstrates Wrong allocation of arrays.
|
||||
// https://gitlab.com/camelot/kickc/-/issues/497
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
.const SIZEOF_POINTER = 2
|
||||
main: {
|
||||
ldx #0
|
||||
__b1:
|
||||
// for(char c=0;c<sizeof(levelRowOff)/sizeof(char*); c++)
|
||||
cpx #$1f*SIZEOF_POINTER/SIZEOF_POINTER
|
||||
bcc __b2
|
||||
// }
|
||||
rts
|
||||
__b2:
|
||||
// levelRowOff[c] = 12345
|
||||
txa
|
||||
asl
|
||||
tay
|
||||
lda #<$3039
|
||||
sta levelRowOff,y
|
||||
lda #>$3039
|
||||
sta levelRowOff+1,y
|
||||
// for(char c=0;c<sizeof(levelRowOff)/sizeof(char*); c++)
|
||||
inx
|
||||
jmp __b1
|
||||
}
|
||||
levelRowOff: .word 1, 2, 3
|
||||
.fill 2*$1c, 0
|
17
src/test/ref/array-16bit-init.cfg
Normal file
17
src/test/ref/array-16bit-init.cfg
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
(void()) main()
|
||||
main: scope:[main] from
|
||||
[0] phi()
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@2
|
||||
[1] (byte) main::c#2 ← phi( main/(byte) 0 main::@2/(byte) main::c#1 )
|
||||
[2] if((byte) main::c#2<(byte) $1f*(const byte) SIZEOF_POINTER/(const byte) SIZEOF_POINTER) goto main::@2
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main::@1
|
||||
[3] return
|
||||
to:@return
|
||||
main::@2: scope:[main] from main::@1
|
||||
[4] (byte~) main::$3 ← (byte) main::c#2 << (byte) 1
|
||||
[5] *((const byte**) levelRowOff + (byte~) main::$3) ← (byte*) 12345
|
||||
[6] (byte) main::c#1 ← ++ (byte) main::c#2
|
||||
to:main::@1
|
333
src/test/ref/array-16bit-init.log
Normal file
333
src/test/ref/array-16bit-init.log
Normal file
@ -0,0 +1,333 @@
|
||||
|
||||
CONTROL FLOW GRAPH SSA
|
||||
|
||||
(void()) main()
|
||||
main: scope:[main] from __start
|
||||
(byte) main::c#0 ← (byte) 0
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@2
|
||||
(byte) main::c#2 ← phi( main/(byte) main::c#0 main::@2/(byte) main::c#1 )
|
||||
(byte~) main::$0 ← sizeof (const byte**) levelRowOff
|
||||
(byte~) main::$1 ← (byte~) main::$0 / (const byte) SIZEOF_POINTER
|
||||
(bool~) main::$2 ← (byte) main::c#2 < (byte~) main::$1
|
||||
if((bool~) main::$2) goto main::@2
|
||||
to:main::@return
|
||||
main::@2: scope:[main] from main::@1
|
||||
(byte) main::c#3 ← phi( main::@1/(byte) main::c#2 )
|
||||
(byte~) main::$3 ← (byte) main::c#3 * (const byte) SIZEOF_POINTER
|
||||
*((const byte**) levelRowOff + (byte~) main::$3) ← ((byte*)) (number) $3039
|
||||
(byte) main::c#1 ← ++ (byte) main::c#3
|
||||
to:main::@1
|
||||
main::@return: scope:[main] from main::@1
|
||||
return
|
||||
to:@return
|
||||
|
||||
(void()) __start()
|
||||
__start: scope:[__start] from
|
||||
call main
|
||||
to:__start::@1
|
||||
__start::@1: scope:[__start] from __start
|
||||
to:__start::@return
|
||||
__start::@return: scope:[__start] from __start::@1
|
||||
return
|
||||
to:@return
|
||||
|
||||
SYMBOL TABLE SSA
|
||||
(const byte) SIZEOF_POINTER = (byte) 2
|
||||
(void()) __start()
|
||||
(label) __start::@1
|
||||
(label) __start::@return
|
||||
(const byte**) levelRowOff[(number) $1f] = { (byte*)(number) 1, (byte*)(number) 2, (byte*)(number) 3 }
|
||||
(void()) main()
|
||||
(byte~) main::$0
|
||||
(byte~) main::$1
|
||||
(bool~) main::$2
|
||||
(byte~) main::$3
|
||||
(label) main::@1
|
||||
(label) main::@2
|
||||
(label) main::@return
|
||||
(byte) main::c
|
||||
(byte) main::c#0
|
||||
(byte) main::c#1
|
||||
(byte) main::c#2
|
||||
(byte) main::c#3
|
||||
|
||||
Inlining cast *((const byte**) levelRowOff + (byte~) main::$3) ← (byte*)(number) $3039
|
||||
Successful SSA optimization Pass2InlineCast
|
||||
Simplifying constant pointer cast (byte*) 1
|
||||
Simplifying constant pointer cast (byte*) 2
|
||||
Simplifying constant pointer cast (byte*) 3
|
||||
Simplifying constant pointer cast (byte*) 12345
|
||||
Successful SSA optimization PassNCastSimplification
|
||||
Alias main::c#2 = main::c#3
|
||||
Successful SSA optimization Pass2AliasElimination
|
||||
Simple Condition (bool~) main::$2 [5] if((byte) main::c#2<(byte~) main::$1) goto main::@2
|
||||
Successful SSA optimization Pass2ConditionalJumpSimplification
|
||||
Constant right-side identified [2] (byte~) main::$0 ← sizeof (const byte**) levelRowOff
|
||||
Successful SSA optimization Pass2ConstantRValueConsolidation
|
||||
Constant (const byte) main::c#0 = 0
|
||||
Constant (const byte) main::$0 = sizeof levelRowOff
|
||||
Successful SSA optimization Pass2ConstantIdentification
|
||||
Removing unused procedure __start
|
||||
Removing unused procedure block __start
|
||||
Removing unused procedure block __start::@1
|
||||
Removing unused procedure block __start::@return
|
||||
Successful SSA optimization PassNEliminateEmptyStart
|
||||
Resolving array sizeof() sizeof (const byte**) levelRowOff
|
||||
Successful SSA optimization PassNSizeOfSimplification
|
||||
Constant right-side identified [1] (byte~) main::$1 ← (const byte) main::$0 / (const byte) SIZEOF_POINTER
|
||||
Successful SSA optimization Pass2ConstantRValueConsolidation
|
||||
Constant (const byte) main::$1 = main::$0/SIZEOF_POINTER
|
||||
Successful SSA optimization Pass2ConstantIdentification
|
||||
Adding number conversion cast (unumber) $1f in
|
||||
Successful SSA optimization PassNAddNumberTypeConversions
|
||||
Simplifying constant integer cast $1f
|
||||
Successful SSA optimization PassNCastSimplification
|
||||
Finalized unsigned number type (byte) $1f
|
||||
Successful SSA optimization PassNFinalizeNumberTypeConversions
|
||||
Rewriting multiplication to use shift [2] (byte~) main::$3 ← (byte) main::c#2 * (const byte) SIZEOF_POINTER
|
||||
Successful SSA optimization Pass2MultiplyToShiftRewriting
|
||||
Inlining constant with var siblings (const byte) main::c#0
|
||||
Constant inlined main::$1 = (byte) $1f*(const byte) SIZEOF_POINTER/(const byte) SIZEOF_POINTER
|
||||
Constant inlined main::c#0 = (byte) 0
|
||||
Constant inlined main::$0 = (byte) $1f*(const byte) SIZEOF_POINTER
|
||||
Successful SSA optimization Pass2ConstantInlining
|
||||
Adding NOP phi() at start of main
|
||||
CALL GRAPH
|
||||
|
||||
Created 1 initial phi equivalence classes
|
||||
Coalesced [7] main::c#4 ← main::c#1
|
||||
Coalesced down to 1 phi equivalence classes
|
||||
Adding NOP phi() at start of main
|
||||
|
||||
FINAL CONTROL FLOW GRAPH
|
||||
|
||||
(void()) main()
|
||||
main: scope:[main] from
|
||||
[0] phi()
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@2
|
||||
[1] (byte) main::c#2 ← phi( main/(byte) 0 main::@2/(byte) main::c#1 )
|
||||
[2] if((byte) main::c#2<(byte) $1f*(const byte) SIZEOF_POINTER/(const byte) SIZEOF_POINTER) goto main::@2
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main::@1
|
||||
[3] return
|
||||
to:@return
|
||||
main::@2: scope:[main] from main::@1
|
||||
[4] (byte~) main::$3 ← (byte) main::c#2 << (byte) 1
|
||||
[5] *((const byte**) levelRowOff + (byte~) main::$3) ← (byte*) 12345
|
||||
[6] (byte) main::c#1 ← ++ (byte) main::c#2
|
||||
to:main::@1
|
||||
|
||||
|
||||
VARIABLE REGISTER WEIGHTS
|
||||
(void()) main()
|
||||
(byte~) main::$3 22.0
|
||||
(byte) main::c
|
||||
(byte) main::c#1 22.0
|
||||
(byte) main::c#2 11.0
|
||||
|
||||
Initial phi equivalence classes
|
||||
[ main::c#2 main::c#1 ]
|
||||
Added variable main::$3 to live range equivalence class [ main::$3 ]
|
||||
Complete equivalence classes
|
||||
[ main::c#2 main::c#1 ]
|
||||
[ main::$3 ]
|
||||
Allocated zp[1]:2 [ main::c#2 main::c#1 ]
|
||||
Allocated zp[1]:3 [ main::$3 ]
|
||||
|
||||
INITIAL ASM
|
||||
Target platform is c64basic / MOS6502X
|
||||
// File Comments
|
||||
// Demonstrates Wrong allocation of arrays.
|
||||
// https://gitlab.com/camelot/kickc/-/issues/497
|
||||
// Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
// Global Constants & labels
|
||||
.const SIZEOF_POINTER = 2
|
||||
// main
|
||||
main: {
|
||||
.label __3 = 3
|
||||
.label c = 2
|
||||
// [1] phi from main to main::@1 [phi:main->main::@1]
|
||||
__b1_from_main:
|
||||
// [1] phi (byte) main::c#2 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1
|
||||
lda #0
|
||||
sta.z c
|
||||
jmp __b1
|
||||
// main::@1
|
||||
__b1:
|
||||
// [2] if((byte) main::c#2<(byte) $1f*(const byte) SIZEOF_POINTER/(const byte) SIZEOF_POINTER) goto main::@2 -- vbuz1_lt_vbuc1_then_la1
|
||||
lda.z c
|
||||
cmp #$1f*SIZEOF_POINTER/SIZEOF_POINTER
|
||||
bcc __b2
|
||||
jmp __breturn
|
||||
// main::@return
|
||||
__breturn:
|
||||
// [3] return
|
||||
rts
|
||||
// main::@2
|
||||
__b2:
|
||||
// [4] (byte~) main::$3 ← (byte) main::c#2 << (byte) 1 -- vbuz1=vbuz2_rol_1
|
||||
lda.z c
|
||||
asl
|
||||
sta.z __3
|
||||
// [5] *((const byte**) levelRowOff + (byte~) main::$3) ← (byte*) 12345 -- qbuc1_derefidx_vbuz1=pbuc2
|
||||
ldy.z __3
|
||||
lda #<$3039
|
||||
sta levelRowOff,y
|
||||
lda #>$3039
|
||||
sta levelRowOff+1,y
|
||||
// [6] (byte) main::c#1 ← ++ (byte) main::c#2 -- vbuz1=_inc_vbuz1
|
||||
inc.z c
|
||||
// [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1]
|
||||
__b1_from___b2:
|
||||
// [1] phi (byte) main::c#2 = (byte) main::c#1 [phi:main::@2->main::@1#0] -- register_copy
|
||||
jmp __b1
|
||||
}
|
||||
// File Data
|
||||
levelRowOff: .word 1, 2, 3
|
||||
.fill 2*$1c, 0
|
||||
|
||||
REGISTER UPLIFT POTENTIAL REGISTERS
|
||||
Statement [4] (byte~) main::$3 ← (byte) main::c#2 << (byte) 1 [ main::c#2 main::$3 ] ( [ main::c#2 main::$3 ] { } ) always clobbers reg byte a
|
||||
Removing always clobbered register reg byte a as potential for zp[1]:2 [ main::c#2 main::c#1 ]
|
||||
Statement [5] *((const byte**) levelRowOff + (byte~) main::$3) ← (byte*) 12345 [ main::c#2 ] ( [ main::c#2 ] { } ) always clobbers reg byte a
|
||||
Statement [4] (byte~) main::$3 ← (byte) main::c#2 << (byte) 1 [ main::c#2 main::$3 ] ( [ main::c#2 main::$3 ] { } ) always clobbers reg byte a
|
||||
Statement [5] *((const byte**) levelRowOff + (byte~) main::$3) ← (byte*) 12345 [ main::c#2 ] ( [ main::c#2 ] { } ) always clobbers reg byte a
|
||||
Potential registers zp[1]:2 [ main::c#2 main::c#1 ] : zp[1]:2 , reg byte x , reg byte y ,
|
||||
Potential registers zp[1]:3 [ main::$3 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
|
||||
|
||||
REGISTER UPLIFT SCOPES
|
||||
Uplift Scope [main] 33: zp[1]:2 [ main::c#2 main::c#1 ] 22: zp[1]:3 [ main::$3 ]
|
||||
Uplift Scope []
|
||||
|
||||
Uplifting [main] best 381 combination reg byte x [ main::c#2 main::c#1 ] reg byte a [ main::$3 ]
|
||||
Uplifting [] best 381 combination
|
||||
|
||||
ASSEMBLER BEFORE OPTIMIZATION
|
||||
// File Comments
|
||||
// Demonstrates Wrong allocation of arrays.
|
||||
// https://gitlab.com/camelot/kickc/-/issues/497
|
||||
// Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
// Global Constants & labels
|
||||
.const SIZEOF_POINTER = 2
|
||||
// main
|
||||
main: {
|
||||
// [1] phi from main to main::@1 [phi:main->main::@1]
|
||||
__b1_from_main:
|
||||
// [1] phi (byte) main::c#2 = (byte) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1
|
||||
ldx #0
|
||||
jmp __b1
|
||||
// main::@1
|
||||
__b1:
|
||||
// [2] if((byte) main::c#2<(byte) $1f*(const byte) SIZEOF_POINTER/(const byte) SIZEOF_POINTER) goto main::@2 -- vbuxx_lt_vbuc1_then_la1
|
||||
cpx #$1f*SIZEOF_POINTER/SIZEOF_POINTER
|
||||
bcc __b2
|
||||
jmp __breturn
|
||||
// main::@return
|
||||
__breturn:
|
||||
// [3] return
|
||||
rts
|
||||
// main::@2
|
||||
__b2:
|
||||
// [4] (byte~) main::$3 ← (byte) main::c#2 << (byte) 1 -- vbuaa=vbuxx_rol_1
|
||||
txa
|
||||
asl
|
||||
// [5] *((const byte**) levelRowOff + (byte~) main::$3) ← (byte*) 12345 -- qbuc1_derefidx_vbuaa=pbuc2
|
||||
tay
|
||||
lda #<$3039
|
||||
sta levelRowOff,y
|
||||
lda #>$3039
|
||||
sta levelRowOff+1,y
|
||||
// [6] (byte) main::c#1 ← ++ (byte) main::c#2 -- vbuxx=_inc_vbuxx
|
||||
inx
|
||||
// [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1]
|
||||
__b1_from___b2:
|
||||
// [1] phi (byte) main::c#2 = (byte) main::c#1 [phi:main::@2->main::@1#0] -- register_copy
|
||||
jmp __b1
|
||||
}
|
||||
// File Data
|
||||
levelRowOff: .word 1, 2, 3
|
||||
.fill 2*$1c, 0
|
||||
|
||||
ASSEMBLER OPTIMIZATIONS
|
||||
Removing instruction jmp __b1
|
||||
Removing instruction jmp __breturn
|
||||
Succesful ASM optimization Pass5NextJumpElimination
|
||||
Removing instruction __b1_from_main:
|
||||
Removing instruction __breturn:
|
||||
Removing instruction __b1_from___b2:
|
||||
Succesful ASM optimization Pass5UnusedLabelElimination
|
||||
|
||||
FINAL SYMBOL TABLE
|
||||
(const byte) SIZEOF_POINTER = (byte) 2
|
||||
(const byte**) levelRowOff[(number) $1f] = { (byte*) 1, (byte*) 2, (byte*) 3 }
|
||||
(void()) main()
|
||||
(byte~) main::$3 reg byte a 22.0
|
||||
(label) main::@1
|
||||
(label) main::@2
|
||||
(label) main::@return
|
||||
(byte) main::c
|
||||
(byte) main::c#1 reg byte x 22.0
|
||||
(byte) main::c#2 reg byte x 11.0
|
||||
|
||||
reg byte x [ main::c#2 main::c#1 ]
|
||||
reg byte a [ main::$3 ]
|
||||
|
||||
|
||||
FINAL ASSEMBLER
|
||||
Score: 321
|
||||
|
||||
// File Comments
|
||||
// Demonstrates Wrong allocation of arrays.
|
||||
// https://gitlab.com/camelot/kickc/-/issues/497
|
||||
// Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
// Global Constants & labels
|
||||
.const SIZEOF_POINTER = 2
|
||||
// main
|
||||
main: {
|
||||
// [1] phi from main to main::@1 [phi:main->main::@1]
|
||||
// [1] phi (byte) main::c#2 = (byte) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1
|
||||
ldx #0
|
||||
// main::@1
|
||||
__b1:
|
||||
// for(char c=0;c<sizeof(levelRowOff)/sizeof(char*); c++)
|
||||
// [2] if((byte) main::c#2<(byte) $1f*(const byte) SIZEOF_POINTER/(const byte) SIZEOF_POINTER) goto main::@2 -- vbuxx_lt_vbuc1_then_la1
|
||||
cpx #$1f*SIZEOF_POINTER/SIZEOF_POINTER
|
||||
bcc __b2
|
||||
// main::@return
|
||||
// }
|
||||
// [3] return
|
||||
rts
|
||||
// main::@2
|
||||
__b2:
|
||||
// levelRowOff[c] = 12345
|
||||
// [4] (byte~) main::$3 ← (byte) main::c#2 << (byte) 1 -- vbuaa=vbuxx_rol_1
|
||||
txa
|
||||
asl
|
||||
// [5] *((const byte**) levelRowOff + (byte~) main::$3) ← (byte*) 12345 -- qbuc1_derefidx_vbuaa=pbuc2
|
||||
tay
|
||||
lda #<$3039
|
||||
sta levelRowOff,y
|
||||
lda #>$3039
|
||||
sta levelRowOff+1,y
|
||||
// for(char c=0;c<sizeof(levelRowOff)/sizeof(char*); c++)
|
||||
// [6] (byte) main::c#1 ← ++ (byte) main::c#2 -- vbuxx=_inc_vbuxx
|
||||
inx
|
||||
// [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1]
|
||||
// [1] phi (byte) main::c#2 = (byte) main::c#1 [phi:main::@2->main::@1#0] -- register_copy
|
||||
jmp __b1
|
||||
}
|
||||
// File Data
|
||||
levelRowOff: .word 1, 2, 3
|
||||
.fill 2*$1c, 0
|
||||
|
13
src/test/ref/array-16bit-init.sym
Normal file
13
src/test/ref/array-16bit-init.sym
Normal file
@ -0,0 +1,13 @@
|
||||
(const byte) SIZEOF_POINTER = (byte) 2
|
||||
(const byte**) levelRowOff[(number) $1f] = { (byte*) 1, (byte*) 2, (byte*) 3 }
|
||||
(void()) main()
|
||||
(byte~) main::$3 reg byte a 22.0
|
||||
(label) main::@1
|
||||
(label) main::@2
|
||||
(label) main::@return
|
||||
(byte) main::c
|
||||
(byte) main::c#1 reg byte x 22.0
|
||||
(byte) main::c#2 reg byte x 11.0
|
||||
|
||||
reg byte x [ main::c#2 main::c#1 ]
|
||||
reg byte a [ main::$3 ]
|
Loading…
x
Reference in New Issue
Block a user