mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-02-20 15:29:10 +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 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));
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
public void testSubExprOptimize1() throws IOException, URISyntaxException {
|
||||
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
|
||||
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:
|
||||
|
@ -107,9 +107,7 @@ bool_and: {
|
||||
txa
|
||||
and #1
|
||||
cpx #$a
|
||||
bcc b5
|
||||
jmp b4
|
||||
b5:
|
||||
bcs b4
|
||||
cmp #0
|
||||
beq b2
|
||||
b4:
|
||||
|
@ -10,8 +10,7 @@ main: {
|
||||
cmp #$20+1
|
||||
bcs b3
|
||||
cmp #$40
|
||||
bcc b3
|
||||
jmp b2
|
||||
bcs b2
|
||||
b3:
|
||||
lda #0
|
||||
b2:
|
||||
|
@ -10,9 +10,7 @@ main: {
|
||||
txa
|
||||
and #$1f
|
||||
cpx #0
|
||||
beq b3
|
||||
jmp b1
|
||||
b3:
|
||||
bne b1
|
||||
cmp #$1f
|
||||
beq b1
|
||||
jmp b1
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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@"
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -8,9 +8,7 @@ main: {
|
||||
b1:
|
||||
lda SCREEN,x
|
||||
cmp #' '
|
||||
bne b2
|
||||
jmp b3
|
||||
b2:
|
||||
beq b3
|
||||
inc SCREEN,x
|
||||
b3:
|
||||
inx
|
||||
|
@ -13,9 +13,7 @@ main: {
|
||||
b2:
|
||||
lda SCREEN,x
|
||||
cmp #' '
|
||||
bne b3
|
||||
jmp b1
|
||||
b3:
|
||||
beq b1
|
||||
inc SCREEN,x
|
||||
jmp b1
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
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