1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-09-08 17:54:40 +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 Pass2ConditionalJumpSimplification(program));
optimizations.add(new Pass2ConditionalAndOrRewriting(program));
optimizations.add(new Pass2ConditionalJumpSequenceImprovement(program));
optimizations.add(new Pass2ConstantRValueConsolidation(program));
optimizations.add(new Pass2ConstantIdentification(program));
optimizations.add(new Pass2ConstantValues(program));

View File

@ -1,6 +1,7 @@
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.types.SymbolType;
import dk.camelot64.kickc.model.values.ConstantInteger;
@ -18,7 +19,7 @@ public class OperatorDecrement extends OperatorUnary {
if(operand instanceof ConstantInteger ) {
return new ConstantInteger(((ConstantInteger) operand).getInteger() -1);
}
throw new CompileError("Calculation not implemented " + getOperator() + " " + operand );
throw new ConstantNotLiteral("Calculation not literal " + getOperator() + " " + operand );
}
@Override

View File

@ -1,6 +1,6 @@
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.types.SymbolType;
import dk.camelot64.kickc.model.values.ConstantInteger;
@ -21,7 +21,7 @@ public class OperatorIncrement extends OperatorUnary {
} else if(operand instanceof ConstantPointer) {
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

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
public void testSubExprOptimize1() throws IOException, URISyntaxException {
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
and #1
cpx #$a
bcc b6
jmp b5
b6:
bcs b5
cmp #0
beq b2
b5:
@ -90,9 +88,7 @@ bool_and: {
txa
and #1
cpx #$a
bcc b5
jmp b4
b5:
bcs b4
cmp #0
beq b2
b4:

View File

@ -107,9 +107,7 @@ bool_and: {
txa
and #1
cpx #$a
bcc b5
jmp b4
b5:
bcs b4
cmp #0
beq b2
b4:

View File

@ -10,8 +10,7 @@ main: {
cmp #$20+1
bcs b3
cmp #$40
bcc b3
jmp b2
bcs b2
b3:
lda #0
b2:

View File

@ -10,9 +10,7 @@ main: {
txa
and #$1f
cpx #0
beq b3
jmp b1
b3:
bne b1
cmp #$1f
beq b1
jmp b1

View File

@ -185,9 +185,7 @@ plexSort: {
sta PLEX_SORTED_IDX+1,x
dex
cpx #$ff
bne b6
jmp b4
b6:
beq b4
lda nxt_y
ldy PLEX_SORTED_IDX,x
cmp PLEX_YPOS,y

View File

@ -95,7 +95,9 @@ pressed: {
ldx #KEY_SPACE
jsr keyboard_key_pressed
cmp #0
beq b1
bne breturn
jmp b1
breturn:
rts
}
// 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
cmp #'@'
bne b2
breturn:
rts
b2:
lda str,x
cmp #' '
bne b3
jmp b4
b3:
beq b4
lda str,x
ldy #0
sta (screen),y
@ -31,7 +30,7 @@ main: {
b4:
inx
cpx #0
bne b1
rts
beq breturn
jmp b1
str: .text "hello brave new world@"
}

View File

@ -12,16 +12,15 @@ main: {
ldy #0
lda (line),y
cmp #'a'
bne b5
bne b3
breturn:
rts
b5:
b3:
ldy #0
b2:
lda (line),y
cmp #'a'
bne b3
jmp b4
b3:
beq b4
lda #'a'
sta (line),y
iny
@ -37,11 +36,11 @@ main: {
!:
lda line+1
cmp #>$400+$28*$19
bcc b1
bne !+
bcc !+
bne breturn
lda line
cmp #<$400+$28*$19
bcc b1
bcs breturn
!:
rts
jmp b1
}

View File

@ -8,13 +8,12 @@ main: {
b1:
lda SCREEN,x
cmp #'a'
bne b2
rts
b2:
beq breturn
lda #'a'
sta SCREEN,x
inx
cpx #$28*6+1
bne b1
breturn:
rts
}

View File

@ -8,9 +8,7 @@ main: {
b1:
lda SCREEN,x
cmp #' '
bne b2
jmp b3
b2:
beq b3
inc SCREEN,x
b3:
inx

View File

@ -13,9 +13,7 @@ main: {
b2:
lda SCREEN,x
cmp #' '
bne b3
jmp b1
b3:
beq b1
inc SCREEN,x
jmp b1
}

View File

@ -129,9 +129,7 @@ plexSort: {
sta PLEX_SORTED_IDX+1,x
dex
cpx #$ff
bne b7
jmp b4
b7:
beq b4
lda nxt_y
ldy PLEX_SORTED_IDX,x
cmp PLEX_YPOS,y
@ -241,9 +239,7 @@ plex_irq: {
sta _4
lda plex_show_idx
cmp #PLEX_COUNT
bcc b7
jmp b4
b7:
bcs b4
cpx _4
bcc b3
b4:

View File

@ -202,9 +202,7 @@ myprintf: {
rts
b3:
cpx #'1'
bcs b39
jmp b4
b39:
bcc b4
cpx #'9'
bcs !b23+
jmp b23
@ -286,10 +284,8 @@ myprintf: {
bne b13
lda bTrailing
cmp #0
beq b41
jmp b15
b41:
lda b
bne b15
tya
cmp bDigits
bcc b16
b15:
@ -304,13 +300,14 @@ myprintf: {
bcc b19
lda bTrailing
cmp #0
bne b42
bne !b22+
jmp b22
b42:
!b22:
lda b
cmp bDigits
bcc b21
bcc !b22+
jmp b22
!b22:
b21:
lda #' '
ldy bLen
@ -393,13 +390,9 @@ myprintf: {
jmp b31
b28:
cpx #$41
bcs b43
jmp b32
b43:
bcc b32
cpx #$5a+1
bcc b37
jmp b32
b37:
bcs b32
txa
axs #-[$20]
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
}