1
0
mirror of https://github.com/dschmenk/PLASMA.git synced 2024-06-10 02:29:30 +00:00
PLASMA/src/toolsrc/codeopt.pla
2019-12-17 21:20:09 -08:00

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