mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-02-22 13:29:18 +00:00
Improved comparisons to avoid branch/jmp sequences by negating comparisons.
This commit is contained in:
parent
06147e2634
commit
9ef7c69c3e
2
src/main/fragment/vbuaa_le_0_then_la1.asm
Normal file
2
src/main/fragment/vbuaa_le_0_then_la1.asm
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
cmp #0
|
||||||
|
beq {la1}
|
2
src/main/fragment/vbuxx_le_0_then_la1.asm
Normal file
2
src/main/fragment/vbuxx_le_0_then_la1.asm
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
cpx #0
|
||||||
|
beq {la1}
|
2
src/main/fragment/vbuyy_le_0_then_la1.asm
Normal file
2
src/main/fragment/vbuyy_le_0_then_la1.asm
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
cpy #0
|
||||||
|
beq {la1}
|
7
src/main/fragment/vwuz1_eq_vwuc1_then_la1.asm
Normal file
7
src/main/fragment/vwuz1_eq_vwuc1_then_la1.asm
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
lda {z1}
|
||||||
|
cmp #<{c1}
|
||||||
|
bne !+
|
||||||
|
lda {z1}+1
|
||||||
|
cmp #>{c1}
|
||||||
|
beq {la1}
|
||||||
|
!:
|
@ -249,6 +249,7 @@ public class Compiler {
|
|||||||
optimizations.add(new Pass2DuplicateRValueIdentification(program));
|
optimizations.add(new Pass2DuplicateRValueIdentification(program));
|
||||||
optimizations.add(new Pass2ConditionalJumpSimplification(program));
|
optimizations.add(new Pass2ConditionalJumpSimplification(program));
|
||||||
optimizations.add(new Pass2ConditionalAndOrRewriting(program));
|
optimizations.add(new Pass2ConditionalAndOrRewriting(program));
|
||||||
|
optimizations.add(new Pass2ConditionalJumpSequenceImprovement(program));
|
||||||
optimizations.add(new Pass2ConstantRValueConsolidation(program));
|
optimizations.add(new Pass2ConstantRValueConsolidation(program));
|
||||||
optimizations.add(new Pass2ConstantIdentification(program));
|
optimizations.add(new Pass2ConstantIdentification(program));
|
||||||
optimizations.add(new Pass2ConstantValues(program));
|
optimizations.add(new Pass2ConstantValues(program));
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package dk.camelot64.kickc.model.operators;
|
package dk.camelot64.kickc.model.operators;
|
||||||
|
|
||||||
import dk.camelot64.kickc.model.CompileError;
|
import dk.camelot64.kickc.model.CompileError;
|
||||||
|
import dk.camelot64.kickc.model.ConstantNotLiteral;
|
||||||
import dk.camelot64.kickc.model.symbols.ProgramScope;
|
import dk.camelot64.kickc.model.symbols.ProgramScope;
|
||||||
import dk.camelot64.kickc.model.types.SymbolType;
|
import dk.camelot64.kickc.model.types.SymbolType;
|
||||||
import dk.camelot64.kickc.model.values.ConstantInteger;
|
import dk.camelot64.kickc.model.values.ConstantInteger;
|
||||||
@ -18,7 +19,7 @@ public class OperatorDecrement extends OperatorUnary {
|
|||||||
if(operand instanceof ConstantInteger ) {
|
if(operand instanceof ConstantInteger ) {
|
||||||
return new ConstantInteger(((ConstantInteger) operand).getInteger() -1);
|
return new ConstantInteger(((ConstantInteger) operand).getInteger() -1);
|
||||||
}
|
}
|
||||||
throw new CompileError("Calculation not implemented " + getOperator() + " " + operand );
|
throw new ConstantNotLiteral("Calculation not literal " + getOperator() + " " + operand );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package dk.camelot64.kickc.model.operators;
|
package dk.camelot64.kickc.model.operators;
|
||||||
|
|
||||||
import dk.camelot64.kickc.model.CompileError;
|
import dk.camelot64.kickc.model.ConstantNotLiteral;
|
||||||
import dk.camelot64.kickc.model.symbols.ProgramScope;
|
import dk.camelot64.kickc.model.symbols.ProgramScope;
|
||||||
import dk.camelot64.kickc.model.types.SymbolType;
|
import dk.camelot64.kickc.model.types.SymbolType;
|
||||||
import dk.camelot64.kickc.model.values.ConstantInteger;
|
import dk.camelot64.kickc.model.values.ConstantInteger;
|
||||||
@ -21,7 +21,7 @@ public class OperatorIncrement extends OperatorUnary {
|
|||||||
} else if(operand instanceof ConstantPointer) {
|
} else if(operand instanceof ConstantPointer) {
|
||||||
return new ConstantPointer(((ConstantPointer) operand).getLocation()+1, ((ConstantPointer) operand).getElementType());
|
return new ConstantPointer(((ConstantPointer) operand).getLocation()+1, ((ConstantPointer) operand).getElementType());
|
||||||
}
|
}
|
||||||
throw new CompileError("Calculation not implemented " + getOperator() + " " + operand );
|
throw new ConstantNotLiteral("Calculation not literal " + getOperator() + " " + operand );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,79 @@
|
|||||||
|
package dk.camelot64.kickc.passes;
|
||||||
|
|
||||||
|
import dk.camelot64.kickc.model.ControlFlowBlock;
|
||||||
|
import dk.camelot64.kickc.model.Program;
|
||||||
|
import dk.camelot64.kickc.model.operators.Operator;
|
||||||
|
import dk.camelot64.kickc.model.operators.Operators;
|
||||||
|
import dk.camelot64.kickc.model.statements.Statement;
|
||||||
|
import dk.camelot64.kickc.model.statements.StatementConditionalJump;
|
||||||
|
import dk.camelot64.kickc.model.values.LabelRef;
|
||||||
|
|
||||||
|
// If a conditional (on block A) jumps to a block (B) that has the same default successor (C) as the current block (A) then
|
||||||
|
// negate the conditional to ensure sequence A->B->C, which generates optimal ASM.
|
||||||
|
public class Pass2ConditionalJumpSequenceImprovement extends Pass2SsaOptimization {
|
||||||
|
|
||||||
|
public Pass2ConditionalJumpSequenceImprovement(Program program) {
|
||||||
|
super(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean step() {
|
||||||
|
boolean modified = false;
|
||||||
|
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
||||||
|
ControlFlowBlock conditionalSuccessor = getGraph().getConditionalSuccessor(block);
|
||||||
|
ControlFlowBlock defaultSuccessor = getGraph().getDefaultSuccessor(block);
|
||||||
|
if(conditionalSuccessor != null && defaultSuccessor != null) {
|
||||||
|
if(conditionalSuccessor.getDefaultSuccessor().equals(defaultSuccessor.getLabel())) {
|
||||||
|
if(conditionalSuccessor.equals(block)) continue;
|
||||||
|
// conditional successor is current blocks default successor
|
||||||
|
// Negate condition and swap conditional/default successor.
|
||||||
|
modified = negateCondition(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean negateCondition(ControlFlowBlock block) {
|
||||||
|
LabelRef defaultSuccessor = block.getDefaultSuccessor();
|
||||||
|
LabelRef conditionalSuccessor = block.getConditionalSuccessor();
|
||||||
|
for(Statement statement : block.getStatements()) {
|
||||||
|
if(statement instanceof StatementConditionalJump) {
|
||||||
|
StatementConditionalJump conditionalJump = (StatementConditionalJump) statement;
|
||||||
|
if(negateOperator(conditionalJump.getOperator()) != null) {
|
||||||
|
Operator negatedOperator = negateOperator(conditionalJump.getOperator());
|
||||||
|
conditionalJump.setOperator(negatedOperator);
|
||||||
|
conditionalJump.setDestination(defaultSuccessor);
|
||||||
|
block.setConditionalSuccessor(defaultSuccessor);
|
||||||
|
block.setDefaultSuccessor(conditionalSuccessor);
|
||||||
|
getLog().append("Negating conditional jump and destination " + conditionalJump.toString(getProgram(), false));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the logic operator that returns the opposite result
|
||||||
|
*
|
||||||
|
* @param operator The operator to negate
|
||||||
|
* @return The opposite logic operator. Null if not found.
|
||||||
|
*/
|
||||||
|
private Operator negateOperator(Operator operator) {
|
||||||
|
if(Operators.EQ.equals(operator))
|
||||||
|
return Operators.NEQ;
|
||||||
|
if(Operators.NEQ.equals(operator))
|
||||||
|
return Operators.EQ;
|
||||||
|
if(Operators.LT.equals(operator))
|
||||||
|
return Operators.GE;
|
||||||
|
if(Operators.LE.equals(operator))
|
||||||
|
return Operators.GT;
|
||||||
|
if(Operators.GT.equals(operator))
|
||||||
|
return Operators.LE;
|
||||||
|
if(Operators.GE.equals(operator))
|
||||||
|
return Operators.LT;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -63,6 +63,11 @@ public class TestPrograms {
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStatementSequence1() throws IOException, URISyntaxException {
|
||||||
|
compileAndCompare("statement-sequence-1");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSubExprOptimize1() throws IOException, URISyntaxException {
|
public void testSubExprOptimize1() throws IOException, URISyntaxException {
|
||||||
compileAndCompare("subexpr-optimize-1");
|
compileAndCompare("subexpr-optimize-1");
|
||||||
|
15
src/test/kc/statement-sequence-1.kc
Normal file
15
src/test/kc/statement-sequence-1.kc
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Tests statement sequence generation
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
const byte* SCREEN = 0x0400;
|
||||||
|
|
||||||
|
for(byte i: 0..10) {
|
||||||
|
byte c = i+5;
|
||||||
|
if((i&1)==0 || i>5)
|
||||||
|
c++;
|
||||||
|
SCREEN[i] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -16,9 +16,7 @@ bool_complex: {
|
|||||||
txa
|
txa
|
||||||
and #1
|
and #1
|
||||||
cpx #$a
|
cpx #$a
|
||||||
bcc b6
|
bcs b5
|
||||||
jmp b5
|
|
||||||
b6:
|
|
||||||
cmp #0
|
cmp #0
|
||||||
beq b2
|
beq b2
|
||||||
b5:
|
b5:
|
||||||
@ -90,9 +88,7 @@ bool_and: {
|
|||||||
txa
|
txa
|
||||||
and #1
|
and #1
|
||||||
cpx #$a
|
cpx #$a
|
||||||
bcc b5
|
bcs b4
|
||||||
jmp b4
|
|
||||||
b5:
|
|
||||||
cmp #0
|
cmp #0
|
||||||
beq b2
|
beq b2
|
||||||
b4:
|
b4:
|
||||||
|
@ -107,9 +107,7 @@ bool_and: {
|
|||||||
txa
|
txa
|
||||||
and #1
|
and #1
|
||||||
cpx #$a
|
cpx #$a
|
||||||
bcc b5
|
bcs b4
|
||||||
jmp b4
|
|
||||||
b5:
|
|
||||||
cmp #0
|
cmp #0
|
||||||
beq b2
|
beq b2
|
||||||
b4:
|
b4:
|
||||||
|
@ -10,8 +10,7 @@ main: {
|
|||||||
cmp #$20+1
|
cmp #$20+1
|
||||||
bcs b3
|
bcs b3
|
||||||
cmp #$40
|
cmp #$40
|
||||||
bcc b3
|
bcs b2
|
||||||
jmp b2
|
|
||||||
b3:
|
b3:
|
||||||
lda #0
|
lda #0
|
||||||
b2:
|
b2:
|
||||||
|
@ -10,9 +10,7 @@ main: {
|
|||||||
txa
|
txa
|
||||||
and #$1f
|
and #$1f
|
||||||
cpx #0
|
cpx #0
|
||||||
beq b3
|
bne b1
|
||||||
jmp b1
|
|
||||||
b3:
|
|
||||||
cmp #$1f
|
cmp #$1f
|
||||||
beq b1
|
beq b1
|
||||||
jmp b1
|
jmp b1
|
||||||
|
@ -185,9 +185,7 @@ plexSort: {
|
|||||||
sta PLEX_SORTED_IDX+1,x
|
sta PLEX_SORTED_IDX+1,x
|
||||||
dex
|
dex
|
||||||
cpx #$ff
|
cpx #$ff
|
||||||
bne b6
|
beq b4
|
||||||
jmp b4
|
|
||||||
b6:
|
|
||||||
lda nxt_y
|
lda nxt_y
|
||||||
ldy PLEX_SORTED_IDX,x
|
ldy PLEX_SORTED_IDX,x
|
||||||
cmp PLEX_YPOS,y
|
cmp PLEX_YPOS,y
|
||||||
|
@ -95,7 +95,9 @@ pressed: {
|
|||||||
ldx #KEY_SPACE
|
ldx #KEY_SPACE
|
||||||
jsr keyboard_key_pressed
|
jsr keyboard_key_pressed
|
||||||
cmp #0
|
cmp #0
|
||||||
beq b1
|
bne breturn
|
||||||
|
jmp b1
|
||||||
|
breturn:
|
||||||
rts
|
rts
|
||||||
}
|
}
|
||||||
// Keyboard row bitmask as expected by CIA#1 Port A when reading a specific keyboard matrix row (rows are numbered 0-7)
|
// Keyboard row bitmask as expected by CIA#1 Port A when reading a specific keyboard matrix row (rows are numbered 0-7)
|
||||||
|
@ -14,13 +14,12 @@ main: {
|
|||||||
lda str,x
|
lda str,x
|
||||||
cmp #'@'
|
cmp #'@'
|
||||||
bne b2
|
bne b2
|
||||||
|
breturn:
|
||||||
rts
|
rts
|
||||||
b2:
|
b2:
|
||||||
lda str,x
|
lda str,x
|
||||||
cmp #' '
|
cmp #' '
|
||||||
bne b3
|
beq b4
|
||||||
jmp b4
|
|
||||||
b3:
|
|
||||||
lda str,x
|
lda str,x
|
||||||
ldy #0
|
ldy #0
|
||||||
sta (screen),y
|
sta (screen),y
|
||||||
@ -31,7 +30,7 @@ main: {
|
|||||||
b4:
|
b4:
|
||||||
inx
|
inx
|
||||||
cpx #0
|
cpx #0
|
||||||
bne b1
|
beq breturn
|
||||||
rts
|
jmp b1
|
||||||
str: .text "hello brave new world@"
|
str: .text "hello brave new world@"
|
||||||
}
|
}
|
||||||
|
@ -12,16 +12,15 @@ main: {
|
|||||||
ldy #0
|
ldy #0
|
||||||
lda (line),y
|
lda (line),y
|
||||||
cmp #'a'
|
cmp #'a'
|
||||||
bne b5
|
bne b3
|
||||||
|
breturn:
|
||||||
rts
|
rts
|
||||||
b5:
|
b3:
|
||||||
ldy #0
|
ldy #0
|
||||||
b2:
|
b2:
|
||||||
lda (line),y
|
lda (line),y
|
||||||
cmp #'a'
|
cmp #'a'
|
||||||
bne b3
|
beq b4
|
||||||
jmp b4
|
|
||||||
b3:
|
|
||||||
lda #'a'
|
lda #'a'
|
||||||
sta (line),y
|
sta (line),y
|
||||||
iny
|
iny
|
||||||
@ -37,11 +36,11 @@ main: {
|
|||||||
!:
|
!:
|
||||||
lda line+1
|
lda line+1
|
||||||
cmp #>$400+$28*$19
|
cmp #>$400+$28*$19
|
||||||
bcc b1
|
bcc !+
|
||||||
bne !+
|
bne breturn
|
||||||
lda line
|
lda line
|
||||||
cmp #<$400+$28*$19
|
cmp #<$400+$28*$19
|
||||||
bcc b1
|
bcs breturn
|
||||||
!:
|
!:
|
||||||
rts
|
jmp b1
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,12 @@ main: {
|
|||||||
b1:
|
b1:
|
||||||
lda SCREEN,x
|
lda SCREEN,x
|
||||||
cmp #'a'
|
cmp #'a'
|
||||||
bne b2
|
beq breturn
|
||||||
rts
|
|
||||||
b2:
|
|
||||||
lda #'a'
|
lda #'a'
|
||||||
sta SCREEN,x
|
sta SCREEN,x
|
||||||
inx
|
inx
|
||||||
cpx #$28*6+1
|
cpx #$28*6+1
|
||||||
bne b1
|
bne b1
|
||||||
|
breturn:
|
||||||
rts
|
rts
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,7 @@ main: {
|
|||||||
b1:
|
b1:
|
||||||
lda SCREEN,x
|
lda SCREEN,x
|
||||||
cmp #' '
|
cmp #' '
|
||||||
bne b2
|
beq b3
|
||||||
jmp b3
|
|
||||||
b2:
|
|
||||||
inc SCREEN,x
|
inc SCREEN,x
|
||||||
b3:
|
b3:
|
||||||
inx
|
inx
|
||||||
|
@ -13,9 +13,7 @@ main: {
|
|||||||
b2:
|
b2:
|
||||||
lda SCREEN,x
|
lda SCREEN,x
|
||||||
cmp #' '
|
cmp #' '
|
||||||
bne b3
|
beq b1
|
||||||
jmp b1
|
|
||||||
b3:
|
|
||||||
inc SCREEN,x
|
inc SCREEN,x
|
||||||
jmp b1
|
jmp b1
|
||||||
}
|
}
|
||||||
|
@ -129,9 +129,7 @@ plexSort: {
|
|||||||
sta PLEX_SORTED_IDX+1,x
|
sta PLEX_SORTED_IDX+1,x
|
||||||
dex
|
dex
|
||||||
cpx #$ff
|
cpx #$ff
|
||||||
bne b7
|
beq b4
|
||||||
jmp b4
|
|
||||||
b7:
|
|
||||||
lda nxt_y
|
lda nxt_y
|
||||||
ldy PLEX_SORTED_IDX,x
|
ldy PLEX_SORTED_IDX,x
|
||||||
cmp PLEX_YPOS,y
|
cmp PLEX_YPOS,y
|
||||||
@ -241,9 +239,7 @@ plex_irq: {
|
|||||||
sta _4
|
sta _4
|
||||||
lda plex_show_idx
|
lda plex_show_idx
|
||||||
cmp #PLEX_COUNT
|
cmp #PLEX_COUNT
|
||||||
bcc b7
|
bcs b4
|
||||||
jmp b4
|
|
||||||
b7:
|
|
||||||
cpx _4
|
cpx _4
|
||||||
bcc b3
|
bcc b3
|
||||||
b4:
|
b4:
|
||||||
|
@ -202,9 +202,7 @@ myprintf: {
|
|||||||
rts
|
rts
|
||||||
b3:
|
b3:
|
||||||
cpx #'1'
|
cpx #'1'
|
||||||
bcs b39
|
bcc b4
|
||||||
jmp b4
|
|
||||||
b39:
|
|
||||||
cpx #'9'
|
cpx #'9'
|
||||||
bcs !b23+
|
bcs !b23+
|
||||||
jmp b23
|
jmp b23
|
||||||
@ -286,10 +284,8 @@ myprintf: {
|
|||||||
bne b13
|
bne b13
|
||||||
lda bTrailing
|
lda bTrailing
|
||||||
cmp #0
|
cmp #0
|
||||||
beq b41
|
bne b15
|
||||||
jmp b15
|
tya
|
||||||
b41:
|
|
||||||
lda b
|
|
||||||
cmp bDigits
|
cmp bDigits
|
||||||
bcc b16
|
bcc b16
|
||||||
b15:
|
b15:
|
||||||
@ -304,13 +300,14 @@ myprintf: {
|
|||||||
bcc b19
|
bcc b19
|
||||||
lda bTrailing
|
lda bTrailing
|
||||||
cmp #0
|
cmp #0
|
||||||
bne b42
|
bne !b22+
|
||||||
jmp b22
|
jmp b22
|
||||||
b42:
|
!b22:
|
||||||
lda b
|
lda b
|
||||||
cmp bDigits
|
cmp bDigits
|
||||||
bcc b21
|
bcc !b22+
|
||||||
jmp b22
|
jmp b22
|
||||||
|
!b22:
|
||||||
b21:
|
b21:
|
||||||
lda #' '
|
lda #' '
|
||||||
ldy bLen
|
ldy bLen
|
||||||
@ -393,13 +390,9 @@ myprintf: {
|
|||||||
jmp b31
|
jmp b31
|
||||||
b28:
|
b28:
|
||||||
cpx #$41
|
cpx #$41
|
||||||
bcs b43
|
bcc b32
|
||||||
jmp b32
|
|
||||||
b43:
|
|
||||||
cpx #$5a+1
|
cpx #$5a+1
|
||||||
bcc b37
|
bcs b32
|
||||||
jmp b32
|
|
||||||
b37:
|
|
||||||
txa
|
txa
|
||||||
axs #-[$20]
|
axs #-[$20]
|
||||||
b32:
|
b32:
|
||||||
|
27
src/test/ref/statement-sequence-1.asm
Normal file
27
src/test/ref/statement-sequence-1.asm
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Tests statement sequence generation
|
||||||
|
.pc = $801 "Basic"
|
||||||
|
:BasicUpstart(main)
|
||||||
|
.pc = $80d "Program"
|
||||||
|
main: {
|
||||||
|
.label SCREEN = $400
|
||||||
|
ldy #0
|
||||||
|
b1:
|
||||||
|
tya
|
||||||
|
tax
|
||||||
|
axs #-[5]
|
||||||
|
tya
|
||||||
|
and #1
|
||||||
|
cmp #0
|
||||||
|
beq b3
|
||||||
|
cpy #5+1
|
||||||
|
bcc b2
|
||||||
|
b3:
|
||||||
|
inx
|
||||||
|
b2:
|
||||||
|
txa
|
||||||
|
sta SCREEN,y
|
||||||
|
iny
|
||||||
|
cpy #$b
|
||||||
|
bne b1
|
||||||
|
rts
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user