1
0
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:
jespergravgaard 2020-07-27 12:12:41 +02:00
parent dcb68b8c1a
commit 9227a3a857
13 changed files with 447 additions and 48 deletions

View File

@ -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"));

View File

@ -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;

View File

@ -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();
}

View File

@ -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));
}
/**

View File

@ -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)) {

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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");

View 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;
}

View 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

View 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

View 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

View 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 ]