1
0
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:
jespergravgaard 2019-05-23 17:50:44 +02:00
parent 06147e2634
commit 9ef7c69c3e
24 changed files with 180 additions and 66 deletions

View File

@ -0,0 +1,2 @@
cmp #0
beq {la1}

View File

@ -0,0 +1,2 @@
cpx #0
beq {la1}

View File

@ -0,0 +1,2 @@
cpy #0
beq {la1}

View File

@ -0,0 +1,7 @@
lda {z1}
cmp #<{c1}
bne !+
lda {z1}+1
cmp #>{c1}
beq {la1}
!:

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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