mirror of
https://github.com/dschmenk/PLASMA.git
synced 2024-06-10 02:29:30 +00:00
591 lines
23 KiB
Plaintext
591 lines
23 KiB
Plaintext
include "inc/cmdsys.plh"
|
|
//
|
|
// Imports from main compiler
|
|
//
|
|
import plasm
|
|
word freeop_lst
|
|
word optimize_seq
|
|
end
|
|
//
|
|
// Code sequence values shares with main compiler
|
|
//
|
|
include "toolsrc/codeseq.plh"
|
|
//
|
|
// Replace all but the first of a series of identical load opcodes by DUP. This
|
|
// doesn't reduce the number of opcodes but does reduce their size in bytes.
|
|
// This is only called on the second optimisation pass because the DUP opcodes
|
|
// may inhibit other peephole optimisations which are more valuable.
|
|
//
|
|
def try_dupify(op)
|
|
byte crunched
|
|
word nextop
|
|
|
|
crunched = FALSE
|
|
nextop = op=>opnext
|
|
while nextop
|
|
if op->opcode <> nextop->opcode; return crunched; fin
|
|
when op->opcode
|
|
is CONST_CODE
|
|
if op=>opval <> nextop=>opval; return crunched; fin
|
|
break
|
|
is LADDR_CODE
|
|
is LLB_CODE
|
|
is LLW_CODE
|
|
if op=>opoffset <> nextop=>opoffset; return crunched; fin
|
|
break
|
|
is GADDR_CODE
|
|
is LAB_CODE
|
|
is LAW_CODE
|
|
if (op=>optag <> nextop=>optag) or (op=>opoffset <> nextop=>opoffset); return crunched; fin
|
|
break
|
|
otherwise
|
|
return crunched
|
|
wend
|
|
nextop->opcode = DUP_CODE
|
|
nextop->opgroup = STACK_GROUP
|
|
nextop = nextop=>opnext
|
|
crunched = TRUE
|
|
loop
|
|
return crunched
|
|
end
|
|
def is_hardware_address(addr)
|
|
return isuge(addr, $C000) and isult(addr, $D000)
|
|
end
|
|
//
|
|
// Crunch sequence (peephole optimize)
|
|
//
|
|
def crunch_seq(seq, pass)
|
|
word nextop, nextopnext, opprev, op, freeops
|
|
byte crunched, shiftcnt
|
|
|
|
opprev = NULL
|
|
op = *seq
|
|
nextop = op=>opnext
|
|
crunched = FALSE
|
|
freeops = 0
|
|
while op and nextop
|
|
when op->opcode
|
|
is CONST_CODE
|
|
if op=>opval == 1
|
|
if nextop->opcode == ADD_CODE
|
|
op->opcode = INC_CODE
|
|
op->opgroup = STACK_GROUP
|
|
freeops = 1
|
|
break
|
|
fin
|
|
if nextop->opcode == SUB_CODE
|
|
op->opcode = DEC_CODE
|
|
op->opgroup = STACK_GROUP
|
|
freeops = 1
|
|
break
|
|
fin
|
|
if nextop->opcode == SHL_CODE
|
|
op->opcode = DUP_CODE
|
|
op->opgroup = STACK_GROUP
|
|
nextop->opcode = ADD_CODE
|
|
break
|
|
fin
|
|
if nextop->opcode == MUL_CODE or nextop->opcode == DIV_CODE
|
|
freeops = -2
|
|
break
|
|
fin
|
|
fin
|
|
when nextop->opcode
|
|
is NEG_CODE
|
|
op=>opval = -op=>opval
|
|
freeops = 1
|
|
break
|
|
is COMP_CODE
|
|
op=>opval = ~op=>opval
|
|
freeops = 1
|
|
break
|
|
is LOGIC_NOT_CODE
|
|
op=>opval = op=>opval ?? FALSE :: TRUE
|
|
freeops = 1
|
|
break
|
|
is BRFALSE_CODE
|
|
if op=>opval
|
|
freeops = -2 // Remove constant and never taken branch
|
|
else
|
|
op->opcode = BRNCH_CODE // Always taken branch
|
|
op->opgroup = RELATIVE_GROUP
|
|
op=>optag = nextop=>optag
|
|
freeops = 1
|
|
fin
|
|
break
|
|
is BRTRUE_CODE
|
|
if not op=>opval
|
|
freeops = -2 // Remove constant never taken branch
|
|
else
|
|
op->opcode = BRNCH_CODE // Always taken branch
|
|
op->opgroup = RELATIVE_GROUP
|
|
op=>optag = nextop=>optag
|
|
freeops = 1
|
|
fin
|
|
break
|
|
is BRGT_CODE
|
|
if opprev and (opprev->opcode == CONST_CODE) and (op=>opval <= opprev=>opval)
|
|
freeops = 1
|
|
fin
|
|
break
|
|
is BRLT_CODE
|
|
if opprev and (opprev->opcode == CONST_CODE) and (op=>opval >= opprev=>opval)
|
|
freeops = 1
|
|
fin
|
|
break
|
|
is BROR_CODE
|
|
if not op=>opval
|
|
freeops = -2 // Remove zero constant
|
|
fin
|
|
break
|
|
is BRAND_CODE
|
|
if op=>opval
|
|
freeops = -2 // Remove non-zero constant
|
|
fin
|
|
break
|
|
is NE_CODE
|
|
if not op=>opval
|
|
freeops = -2 // Remove ZERO:ISNE
|
|
fin
|
|
break
|
|
is EQ_CODE
|
|
if not op=>opval
|
|
op->opcode = LOGIC_NOT_CODE // Replace ZERO:ISEQ
|
|
op->opgroup = STACK_GROUP
|
|
freeops = 1
|
|
fin
|
|
break
|
|
is CONST_CODE // Collapse constant operation
|
|
nextopnext = nextop=>opnext
|
|
if nextopnext
|
|
when nextopnext->opcode
|
|
is MUL_CODE
|
|
op=>opval = op=>opval * nextop=>opval
|
|
freeops = 2
|
|
break
|
|
is DIV_CODE
|
|
op=>opval = op=>opval / nextop=>opval
|
|
freeops = 2
|
|
break
|
|
is MOD_CODE
|
|
op=>opval = op=>opval % nextop=>opval
|
|
freeops = 2
|
|
break
|
|
is ADD_CODE
|
|
op=>opval = op=>opval + nextop=>opval
|
|
freeops = 2
|
|
break
|
|
is SUB_CODE
|
|
op=>opval = op=>opval - nextop=>opval
|
|
freeops = 2
|
|
break
|
|
is SHL_CODE
|
|
op=>opval = op=>opval << nextop=>opval
|
|
freeops = 2
|
|
break
|
|
is SHR_CODE
|
|
op=>opval = op=>opval >> nextop=>opval
|
|
freeops = 2
|
|
break
|
|
is AND_CODE
|
|
op=>opval = op=>opval & nextop=>opval
|
|
freeops = 2
|
|
break
|
|
is OR_CODE
|
|
op=>opval = op=>opval | nextop=>opval
|
|
freeops = 2
|
|
break
|
|
is EOR_CODE
|
|
op=>opval = op=>opval ^ nextop=>opval
|
|
freeops = 2
|
|
break
|
|
is EQ_CODE
|
|
op=>opval = op=>opval == nextop=>opval
|
|
freeops = 2
|
|
break
|
|
is NE_CODE
|
|
op=>opval = op=>opval <> nextop=>opval
|
|
freeops = 2
|
|
break
|
|
is GE_CODE
|
|
op=>opval = op=>opval >= nextop=>opval
|
|
freeops = 2
|
|
break
|
|
is LT_CODE
|
|
op=>opval = op=>opval < nextop=>opval
|
|
freeops = 2
|
|
break
|
|
is GT_CODE
|
|
op=>opval = op=>opval > nextop=>opval
|
|
freeops = 2
|
|
break
|
|
is LE_CODE
|
|
op=>opval = op=>opval <= nextop=>opval
|
|
freeops = 2
|
|
break
|
|
wend // End of collapse constant operation
|
|
fin
|
|
if pass and not freeops and op=>opval
|
|
crunched = try_dupify(op)
|
|
fin
|
|
break // CONST_CODE
|
|
is ADD_CODE
|
|
if op=>opval == 0
|
|
freeops = -2
|
|
elsif op=>opval > 0 and op=>opval <= 255
|
|
op->opcode = ADDI_CODE
|
|
freeops = 1
|
|
elsif op=>opval >= -255 and op=>opval < 0
|
|
op->opcode = SUBI_CODE
|
|
op=>opval = -op=>opval
|
|
freeops = 1
|
|
fin
|
|
break
|
|
is SUB_CODE
|
|
if op=>opval == 0
|
|
freeops = -2
|
|
elsif op=>opval > 0 and op=>opval <= 255
|
|
op->opcode = SUBI_CODE
|
|
freeops = 1
|
|
elsif op=>opval >= -255 and op=>opval < 0
|
|
op->opcode = ADDI_CODE
|
|
op=>opval = -op=>opval
|
|
freeops = 1
|
|
fin
|
|
break
|
|
is AND_CODE
|
|
if op=>opval >= 0 and op=>opval <= 255
|
|
op->opcode = ANDI_CODE
|
|
freeops = 1
|
|
fin
|
|
break
|
|
is OR_CODE
|
|
if op=>opval == 0
|
|
freeops = -2
|
|
elsif op=>opval > 0 and op=>opval <= 255
|
|
op->opcode = ORI_CODE
|
|
freeops = 1
|
|
fin
|
|
break
|
|
is MUL_CODE
|
|
if op=>opval == 0
|
|
op->opcode = DROP_CODE
|
|
op->opgroup = STACK_GROUP
|
|
nextop->opcode = CONST_CODE
|
|
nextop->opgroup = CONST_GROUP
|
|
nextop=>opval = 0
|
|
elsif op=>opval == 2
|
|
op->opcode = DUP_CODE
|
|
op->opgroup = STACK_GROUP
|
|
nextop->opcode = ADD_CODE
|
|
else
|
|
for shiftcnt = 2 to 15
|
|
if op=>opval == 1 << shiftcnt
|
|
op=>opval = shiftcnt
|
|
nextop->opcode = SHL_CODE
|
|
break
|
|
fin
|
|
next
|
|
fin
|
|
break
|
|
is DIV_CODE
|
|
for shiftcnt = 1 to 15
|
|
if op=>opval == 1 << shiftcnt
|
|
op=>opval = shiftcnt
|
|
nextop->opcode = SHR_CODE
|
|
break
|
|
fin
|
|
next
|
|
break
|
|
wend
|
|
break // CONST_CODE
|
|
is LADDR_CODE
|
|
when nextop->opcode
|
|
is CONST_CODE
|
|
if nextop=>opnext
|
|
nextopnext = nextop=>opnext
|
|
when nextopnext->opcode
|
|
is ADD_CODE // INDEXB_CODE
|
|
op=>opoffset = op=>opoffset + nextop=>opval
|
|
freeops = 2
|
|
break
|
|
is INDEXW_CODE
|
|
op=>opoffset = op=>opoffset + nextop=>opval * 2
|
|
freeops = 2
|
|
break
|
|
wend
|
|
fin
|
|
break
|
|
is LB_CODE
|
|
op->opcode = LLB_CODE
|
|
freeops = 1
|
|
break
|
|
is LW_CODE
|
|
op->opcode = LLW_CODE
|
|
freeops = 1
|
|
break
|
|
is SB_CODE
|
|
op->opcode = SLB_CODE
|
|
freeops = 1
|
|
break
|
|
is SW_CODE
|
|
op->opcode = SLW_CODE
|
|
freeops = 1
|
|
break
|
|
wend
|
|
if pass > 0 and not freeops
|
|
crunched = try_dupify(op)
|
|
fin
|
|
break // LADDR_CODE
|
|
is GADDR_CODE
|
|
when nextop->opcode
|
|
is CONST_CODE
|
|
if nextop=>opnext
|
|
nextopnext = nextop=>opnext
|
|
when nextopnext->opcode
|
|
is ADD_CODE // INDEXB_CODE
|
|
op=>opoffset = op=>opoffset + nextop=>opval
|
|
freeops = 2
|
|
break
|
|
is INDEXW_CODE
|
|
op=>opoffset = op=>opoffset + nextop=>opval * 2
|
|
freeops = 2
|
|
break
|
|
wend
|
|
fin
|
|
break
|
|
is LB_CODE
|
|
op->opcode = LAB_CODE
|
|
freeops = 1
|
|
break
|
|
is LW_CODE
|
|
op->opcode = LAW_CODE
|
|
freeops = 1
|
|
break
|
|
is SB_CODE
|
|
op->opcode = SAB_CODE
|
|
freeops = 1
|
|
break
|
|
is SW_CODE
|
|
op->opcode = SAW_CODE
|
|
freeops = 1
|
|
break
|
|
is ICAL_CODE
|
|
op->opcode = CALL_CODE
|
|
freeops = 1
|
|
break
|
|
wend
|
|
if pass and not freeops
|
|
crunched = try_dupify(op)
|
|
fin
|
|
break // GADDR_CODE
|
|
is LLB_CODE
|
|
when nextop->opcode
|
|
is ADD_CODE // INDEXB_CODE
|
|
op->opcode = ADDLB_CODE
|
|
freeops = 1
|
|
break
|
|
is INDEXW_CODE
|
|
op->opcode = IDXLB_CODE
|
|
freeops = 1
|
|
break
|
|
wend
|
|
if pass and not freeops
|
|
crunched = try_dupify(op)
|
|
fin
|
|
break // LLB_CODE
|
|
is LLW_CODE
|
|
when nextop->opcode
|
|
is ADD_CODE // INDEXB_CODE
|
|
op->opcode = ADDLW_CODE
|
|
freeops = 1
|
|
break
|
|
is INDEXW_CODE
|
|
op->opcode = IDXLW_CODE
|
|
freeops = 1
|
|
break
|
|
is CONST_CODE
|
|
// LLW [n]:CB 8:SHR -> LLB [n+1]
|
|
if nextop=>opval == 8 and nextop=>opnext
|
|
nextopnext = nextop=>opnext
|
|
if nextopnext->opcode == SHR_CODE
|
|
op->opcode = LLB_CODE
|
|
op=>opoffset++
|
|
freeops = 2
|
|
break
|
|
fin
|
|
fin
|
|
break
|
|
wend
|
|
if pass and not freeops
|
|
crunched = try_dupify(op)
|
|
fin
|
|
break // LLW_CODE
|
|
is LAB_CODE
|
|
when nextop->opcode
|
|
is ADD_CODE // INDEXB_CODE
|
|
op->opcode = ADDAB_CODE
|
|
freeops = 1
|
|
break
|
|
is INDEXW_CODE
|
|
op->opcode = IDXAB_CODE
|
|
freeops = 1
|
|
break
|
|
wend
|
|
if pass and not freeops and not is_hardware_address(op=>opoffset)
|
|
crunched = try_dupify(op)
|
|
fin
|
|
break // LAB_CODE
|
|
is LAW_CODE
|
|
when nextop->opcode
|
|
is ADD_CODE // INDEXB_CODE
|
|
op->opcode = ADDAW_CODE
|
|
freeops = 1
|
|
break
|
|
is INDEXW_CODE
|
|
op->opcode = IDXAW_CODE
|
|
freeops = 1
|
|
break
|
|
is CONST_CODE
|
|
// LLW [n]:CB 8:SHR -> LLB [n+1]
|
|
if nextop=>opval == 8 and nextop=>opnext
|
|
nextopnext = nextop=>opnext
|
|
if nextopnext->opcode == SHR_CODE
|
|
op->opcode = LAB_CODE
|
|
op=>opoffset++
|
|
freeops = 2
|
|
break
|
|
fin
|
|
fin
|
|
break
|
|
wend
|
|
if pass and not freeops and not is_hardware_address(op=>opoffset)
|
|
crunched = try_dupify(op)
|
|
fin
|
|
break // LAW_CODE
|
|
is LOGIC_NOT_CODE
|
|
when nextop->opcode
|
|
is BRFALSE_CODE
|
|
op->opcode = BRTRUE_CODE
|
|
op->opgroup = RELATIVE_GROUP
|
|
op=>optag = nextop=>optag
|
|
freeops = 1
|
|
break
|
|
is BRTRUE_CODE
|
|
op->opcode = BRFALSE_CODE
|
|
op->opgroup = RELATIVE_GROUP
|
|
op=>optag = nextop=>optag
|
|
freeops = 1
|
|
break
|
|
wend
|
|
break // LOGIC_NOT_CODE
|
|
is EQ_CODE
|
|
when nextop->opcode
|
|
is BRFALSE_CODE
|
|
op->opcode = BRNE_CODE
|
|
op->opgroup = RELATIVE_GROUP
|
|
op=>optag = nextop=>optag
|
|
freeops = 1
|
|
break
|
|
is BRTRUE_CODE
|
|
op->opcode = BREQ_CODE
|
|
op->opgroup = RELATIVE_GROUP
|
|
op=>optag = nextop=>optag
|
|
freeops = 1
|
|
break
|
|
wend
|
|
break // EQ_CODE
|
|
is NE_CODE
|
|
when nextop->opcode
|
|
is BRFALSE_CODE
|
|
op->opcode = BREQ_CODE
|
|
op->opgroup = RELATIVE_GROUP
|
|
op=>optag = nextop=>optag
|
|
freeops = 1
|
|
break
|
|
is BRTRUE_CODE
|
|
op->opcode = BRNE_CODE
|
|
op->opgroup = RELATIVE_GROUP
|
|
op=>optag = nextop=>optag
|
|
freeops = 1
|
|
break
|
|
wend
|
|
break // NE_CODE
|
|
is SLB_CODE
|
|
if nextop->opcode == LLB_CODE and op=>opoffset == nextop=>opoffset
|
|
op->opcode = DLB_CODE
|
|
freeops = 1
|
|
fin
|
|
break // SLB_CODE
|
|
is SLW_CODE
|
|
if nextop->opcode == LLW_CODE and op=>opoffset == nextop=>opoffset
|
|
op->opcode = DLW_CODE
|
|
freeops = 1
|
|
fin
|
|
break // SLW_CODE
|
|
is SAB_CODE
|
|
if nextop->opcode == LAB_CODE and op=>optag == nextop=>optag and op=>opoffset == nextop=>opoffset
|
|
op->opcode = DAB_CODE
|
|
freeops = 1
|
|
fin
|
|
break // SAB_CODE
|
|
is SAW_CODE
|
|
if nextop->opcode == LAW_CODE and op=>optag == nextop=>optag and op=>opoffset == nextop=>opoffset
|
|
op->opcode = DAW_CODE
|
|
freeops = 1
|
|
fin
|
|
break // SAW_CODE
|
|
wend
|
|
//
|
|
// Free up crunched ops. If freeops is positive we free up that many ops
|
|
// *after* op; if it's negative, we free up abs(freeops) ops *starting
|
|
// with* op.
|
|
//
|
|
if freeops < 0
|
|
freeops = -freeops
|
|
if op == *seq
|
|
//
|
|
// If op is at the start of the sequence, we treat this as a special case.
|
|
//
|
|
while freeops
|
|
nextop = op=>opnext
|
|
op=>opnext = freeop_lst
|
|
freeop_lst = op
|
|
*seq = nextop
|
|
op = nextop
|
|
freeops--
|
|
loop
|
|
crunched = TRUE
|
|
else
|
|
//
|
|
// Otherwise we just move op back to point to the previous op and
|
|
// let the following loop remove the required number of ops.
|
|
//
|
|
op = opprev
|
|
nextop = op=>opnext
|
|
fin
|
|
fin
|
|
while freeops
|
|
op=>opnext = nextop=>opnext
|
|
nextop=>opnext = freeop_lst
|
|
freeop_lst = nextop
|
|
nextop = op=>opnext
|
|
crunched = TRUE
|
|
freeops--
|
|
loop
|
|
opprev = op
|
|
op = nextop
|
|
nextop = op=>opnext
|
|
loop
|
|
return crunched
|
|
end
|
|
//
|
|
// Point to crunch function
|
|
//
|
|
optimize_seq = @crunch_seq
|
|
//
|
|
// Keep this module in memory
|
|
//
|
|
return modkeep
|
|
done
|