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 elsif op=>opval == 3 op->opcode = DUP_CODE op->opgroup = STACK_GROUP nextop->opcode = INDEXW_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