1
0
mirror of https://github.com/dschmenk/PLASMA.git synced 2024-08-21 06:29:07 +00:00

Modular optimizer

This commit is contained in:
David Schmenk 2018-01-03 08:54:46 -08:00
parent ead253ef14
commit a8f7149bb7
5 changed files with 787 additions and 738 deletions

View File

@ -53,6 +53,7 @@ FIBER = FIBER\#FE1000
LONGJMP = LONGJMP\#FE1000
PLASM = plasm
PLASMAPLASM = PLASM\#FE1000
CODEOPT = CODEOPT\#FE1000
INCS = toolsrc/plasm.h toolsrc/tokens.h toolsrc/symbols.h toolsrc/lex.h toolsrc/parse.h toolsrc/codegen.h
OBJS = toolsrc/plasm.c toolsrc/parse.c toolsrc/lex.c toolsrc/codegen.c
#
@ -71,7 +72,7 @@ TXTTYPE = .TXT
#SYSTYPE = \#FF2000
#TXTTYPE = \#040000
all: $(PLASM) $(PLVM) $(PLVM01) $(PLVM02) $(PLVM802) $(PLVM03) $(CMD) $(PLASMAPLASM) $(ARGS) $(MEMMGR) $(MEMTEST) $(FIBER) $(LONGJMP) $(ED) $(MON) $(ROD) $(SIEVE) $(UTHERNET2) $(UTHERNET) $(ETHERIP) $(INET) $(DHCP) $(HTTPD) $(ROGUE) $(ROGUEMAP) $(ROGUECOMBAT) $(ROGUEIO) $(HGR1) $(TONE) $(DGR) $(DGRTEST) $(FILEIO) $(CONIO) $(PORTIO) $(SPIPORT) $(SDFAT) $(FATCAT) $(FATGET) $(FATPUT) $(FATWDSK) $(FATRDSK) $(SANE) $(FPSTR) $(FPU) $(SANITY) $(RPNCALC)
all: $(PLASM) $(PLVM) $(PLVM01) $(PLVM02) $(PLVM802) $(PLVM03) $(CMD) $(PLASMAPLASM) $(CODEOPT) $(ARGS) $(MEMMGR) $(MEMTEST) $(FIBER) $(LONGJMP) $(ED) $(MON) $(ROD) $(SIEVE) $(UTHERNET2) $(UTHERNET) $(ETHERIP) $(INET) $(DHCP) $(HTTPD) $(ROGUE) $(ROGUEMAP) $(ROGUECOMBAT) $(ROGUEIO) $(HGR1) $(TONE) $(DGR) $(DGRTEST) $(FILEIO) $(CONIO) $(PORTIO) $(SPIPORT) $(SDFAT) $(FATCAT) $(FATGET) $(FATPUT) $(FATWDSK) $(FATRDSK) $(SANE) $(FPSTR) $(FPU) $(SANITY) $(RPNCALC)
clean:
-rm *FE1000 *FF2000 $(PLASM) $(PLVM) $(PLVM01) $(PLVM02) $(PLVM03)
@ -85,10 +86,14 @@ clean:
$(PLASM): $(OBJS) $(INCS)
cc $(OBJS) -o $(PLASM)
$(PLASMAPLASM): toolsrc/plasm.pla toolsrc/lex.pla toolsrc/parse.pla toolsrc/codegen.pla
$(PLASMAPLASM): toolsrc/plasm.pla toolsrc/lex.pla toolsrc/parse.pla toolsrc/codegen.pla toolsrc/codeseq.plh
./$(PLASM) -AMOW < toolsrc/plasm.pla > toolsrc/plasm.a
acme --setpc 4094 -o $(PLASMAPLASM) toolsrc/plasm.a
$(CODEOPT): toolsrc/codeopt.pla toolsrc/codeseq.plh
./$(PLASM) -AMOW < toolsrc/codeopt.pla > toolsrc/codeopt.a
acme --setpc 4094 -o $(CODEOPT) toolsrc/codeopt.a
#
# PLASMA VMs
#

View File

@ -24,6 +24,51 @@
// loop
//end
//
// New/release sequence ops
//
def new_op
word op
op = freeop_lst
if not op
puts("Compiler out of sequence ops!")
return NULL
fin
freeop_lst = freeop_lst=>opnext
op=>opnext = NULL
return op
end
def release_op(op)#0
if op
op=>opnext = freeop_lst
freeop_lst = op
fin
end
def release_seq(seq)#0
word op
while seq
op = seq
seq = seq=>opnext
//
//Free this op
//
op=>opnext = freeop_lst
freeop_lst = op
loop
end
//
// Append one sequence to the end of another
//
def cat_seq(seq1, seq2)
word op
if not seq1; return seq2; fin
op = seq1
while op=>opnext; op = op=>opnext; loop
op=>opnext = seq2
return seq1
end
//
// Emit data/bytecode
//
def emit_byte(bval)#0
@ -212,6 +257,141 @@ def emit_ctag(ctag)#0
ctag_tbl=>[ctag] = (codeptr - codebuff) | IS_RESOLVED
end
//
// Emit the pending sequence
//
def emit_pending_seq#0
word lcl_pending, op
//
// This is called by some of the emit_*() functions to ensure that any
// pending ops are emitted before they emit their own op when they are
// called from the parser. However, this function itself calls some of those
// emit_*() functions to emit instructions from the pending sequence, which
// would cause an infinite loop if we weren't careful. We therefore set
// pending_seq to null on entry and work with a local copy, so if this
// function calls back into itself it is a no-op.
//
if not pending_seq; return; fin
lcl_pending = pending_seq
pending_seq = NULL
if outflags & OPTIMIZE
while optimize_seq(@lcl_pending, 0); loop
if outflags & OPTIMIZE2
while optimize_seq(@lcl_pending, 1); loop
fin
fin
while lcl_pending
op = lcl_pending
when op->opgroup
//
// Constant value
//
is CONST_GROUP
if op=>opval == $0000 // ZERO
emit_byte($00)
elsif op=>opval & $FF00 == $0000 // Constant BYTE
emit_byte($2A)
emit_byte(op->opval)
elsif op=>opval & $FF00 == $FF00 // Constant $FF00 | BYTE
emit_byte($5E)
emit_byte(op->opval)
else // Constant WORD
emit_byte($2C)
emit_word(op=>opval)
fin
break
//
// Constant string
//
is CONSTR_GROUP
emit_byte($2E)
emit_data(0, STR_TYPE, op=>opval, 0)
break
//
// Single op codes
//
is STACK_GROUP
emit_byte(op->opcode)
break
//
// Local address codes
//
is LOCAL_GROUP
emit_byte(op->opcode)
emit_byte(op->opoffset)
break
//
// Global address codes
//
is GLOBAL_GROUP
if op=>optag & IS_CTAG and op=>opoffset
exit_err(ERR_INVAL|ERR_CODE|ERR_CONST)
else
emit_byte(op->opcode)
emit_addr(op=>optag+op=>opoffset)
fin
break
//
// Relative address codes
//
is RELATIVE_GROUP
emit_byte(op->opcode)
emit_reladdr(op=>optag)
break
//
// Code tags
//
is CODETAG_GROUP
emit_ctag(op=>optag)
break
otherwise
return
wend
lcl_pending = lcl_pending=>opnext;
//
// Free this op
//
op=>opnext = freeop_lst
freeop_lst = op
loop
end
//
// Emit a sequence of ops (into the pending sequence)
//
def emit_seq(seq)#0
word op
byte string
string = FALSE
op = seq
while op
if op->opgroup == CONSTR_GROUP; string = TRUE; break; fin
op = op=>opnext
loop
pending_seq = cat_seq(pending_seq, seq)
//
// The source code comments in the output are much more logical if we don't
// merge multiple sequences together. There's no value in doing this merging
// if we're not optimizing, and we optionally allow it to be prevented even
// when we are optimizing by specifing the -N (NO_COMBINE) flag.
//
// We must also force output if the sequence includes a CS opcode, as the
// associated 'constant' is only temporarily valid.
//
if not (outflags & (OPTIMIZE|OPTIMIZE2)) or (outflags & NO_COMBINE) or string
emit_pending_seq
fin
end
//
// Emit lambda function
//
def emit_lambdafunc(tag, namestr, cparms, lambda_seq)#0
emit_ctag(tag)
framesize = cparms * 2
emit_enter(cparms)
emit_seq(lambda_seq)
emit_pending_seq
emit_leave
end
//
// ID manager
//
def idmatch(nameptr, len, idptr, idcnt)
@ -367,503 +547,6 @@ def ctag_new
return codetag | IS_CTAG
end
//
// New/release sequence ops
//
def new_op
word op
op = freeop_lst
if not op
puts("Compiler out of sequence ops!")
return NULL
fin
freeop_lst = freeop_lst=>opnext
op=>opnext = NULL
return op
end
def release_op(op)#0
if op
op=>opnext = freeop_lst
freeop_lst = op
fin
end
def release_seq(seq)#0
word op
while seq
op = seq
seq = seq=>opnext
//
//Free this op
//
op=>opnext = freeop_lst
freeop_lst = op
loop
end
//
// 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
byte crunched, freeops, 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
freeops = 1
break
fin
if nextop->opcode == SUB_CODE
op->opcode = DEC_CODE
freeops = 1
break
fin
if nextop->opcode == SHL_CODE
op->opcode = DUP_CODE
nextop->opcode = ADD_CODE
crunched = 1
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 LB_CODE // BPTR_CODE
op=>opoffset = op=>opval
op->opcode = LAB_CODE
op->opgroup = GLOBAL_GROUP
freeops = 1
break
is LW_CODE // WPTR_CODE
op=>opoffset = op=>opval
op->opcode = LAW_CODE
op->opgroup = LOCAL_GROUP
freeops = 1
break
is SB_CODE
op=>opoffset = op=>opval
op->opcode = SAB_CODE
op->opgroup = GLOBAL_GROUP
freeops = 1
break
is SW_CODE
op=>opoffset = op=>opval
op->opcode = SAW_CODE
op->opgroup = GLOBAL_GROUP
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 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
freeops = 1
fin
break
is CONST_CODE // Collapse constant operation
nextopnext = nextop->nextop
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
is LOGIC_OR_CODE
op=>opval = op=>opval or nextop=>opval
freeops = 2
break
is LOGIC_AND_CODE
op=>opval = op=>opval and nextop=>opval
freeops = 2
break
wend // End of collapse constant operation
fin
if pass > 0 and freeops == 0 and op=>opval
crunched = try_dupify(op)
fin
break // CONST_CODE
is MUL_CODE
for shiftcnt = 0 to 15
if op=>opval == 1 << shiftcnt
op=>opval = shiftcnt
nextop->opcode = SHL_CODE
nextop->opgroup = STACK_GROUP
break
fin
next
break
is DIV_CODE
for shiftcnt = 0 to 15
if op=>opval == 1 << shiftcnt
op=>opval = shiftcnt
nextop->opcode = SHR_CODE
nextop->opcode = STACK_GROUP
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
is 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
op->opgroup = LOCAL_GROUP
freeops = 1
break
is LW_CODE
op->opcode = LLW_CODE
op->opgroup = LOCAL_GROUP
freeops = 1
break
is SB_CODE
op->opcode = SLB_CODE
op->opgroup = LOCAL_GROUP
freeops = 1
break
is SW_CODE
op->opcode = SLW_CODE
op->opgroup = LOCAL_GROUP
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
is 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
op->opgroup = GLOBAL_GROUP
freeops = 1
break
is LW_CODE
op->opcode = LAW_CODE
op->opgroup = GLOBAL_GROUP
freeops = 1
break
is SB_CODE
op->opcode = SAB_CODE
op->opgroup = GLOBAL_GROUP
freeops = 1
break
is SW_CODE
op->opcode = SAW_CODE
op->opgroup = GLOBAL_GROUP
freeops = 1
break
is ICAL_CODE
op->opcode = CALL_CODE
op->opgroup = GLOBAL_GROUP
freeops = 1
break
wend
if pass > 0 and not freeops
crunched = try_dupify(op)
fin
break // GADDR_CODE
is LLB_CODE
if pass > 0
crunched = try_dupify(op)
fin
break // LLB_CODE
is LLW_CODE
// LLW [n]:CB 8:SHR -> LLB [n+1]
if nextop->opcode == CONST_CODE and nextop=>opval == 8
if nextop=>opnext
nextopnext = nextop=>opnext
if nextopnext->opcode == SHR_CODE
op->opcode = LLB_CODE
op=>opoffset++
freeops = 2
break
fin
fin
fin
if pass and not freeops
crunched = try_dupify(op)
fin
break // LLW_CODE
is LAB_CODE
if pass and not is_hardware_address(op=>opoffset)
crunched = try_dupify(op)
fin
break // LAB_CODE
is LAW_CODE
// LAW x:CB 8:SHR -> LAB x+1
if nextop->opcode == CONST_CODE and nextop=>opval == 8
if nextop=>opnext
nextopnext = nextop=>opnext
if nextopnext->opcode == SHR_CODE
op->opcode = LAB_CODE
op=>opoffset++
freeops = 2
break
fin
fin
fin
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 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 > 0
nextop = op=>opnext
release_op(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
//
// Generate/add to a sequence of code
//
def gen_op(seq, code)
@ -1076,150 +759,3 @@ def gen_bop(tkn, seq)
op->opgroup = STACK_GROUP
return seq
end
//
// Append one sequence to the end of another
//
def cat_seq(seq1, seq2)
word op
if not seq1; return seq2; fin
op = seq1
while op=>opnext; op = op=>opnext; loop
op=>opnext = seq2
return seq1
end
//
// Emit the pending sequence
//
def emit_pending_seq#0
word lcl_pending, op
//
// This is called by some of the emit_*() functions to ensure that any
// pending ops are emitted before they emit their own op when they are
// called from the parser. However, this function itself calls some of those
// emit_*() functions to emit instructions from the pending sequence, which
// would cause an infinite loop if we weren't careful. We therefore set
// pending_seq to null on entry and work with a local copy, so if this
// function calls back into itself it is a no-op.
//
if not pending_seq; return; fin
lcl_pending = pending_seq
pending_seq = NULL
if outflags & OPTIMIZE
while crunch_seq(@lcl_pending, 0); loop
if outflags & OPTIMIZE2
while crunch_seq(@lcl_pending, 1); loop
fin
fin
while lcl_pending
op = lcl_pending
when op->opgroup
//
// Constant value
//
is CONST_GROUP
if op=>opval == $0000 // ZERO
emit_byte($00)
elsif op=>opval & $FF00 == $0000 // Constant BYTE
emit_byte($2A)
emit_byte(op->opval)
elsif op=>opval & $FF00 == $FF00 // Constant $FF00 | BYTE
emit_byte($5E)
emit_byte(op->opval)
else // Constant WORD
emit_byte($2C)
emit_word(op=>opval)
fin
break
//
// Constant string
//
is CONSTR_GROUP
emit_byte($2E)
emit_data(0, STR_TYPE, op=>opval, 0)
break
//
// Single op codes
//
is STACK_GROUP
emit_byte(op->opcode)
break
//
// Local address codes
//
is LOCAL_GROUP
emit_byte(op->opcode)
emit_byte(op->opoffset)
break
//
// Global address codes
//
is GLOBAL_GROUP
if op=>optag & IS_CTAG and op=>opoffset
exit_err(ERR_INVAL|ERR_CODE|ERR_CONST)
else
emit_byte(op->opcode)
emit_addr(op=>optag+op=>opoffset)
fin
break
//
// Relative address codes
//
is RELATIVE_GROUP
emit_byte(op->opcode)
emit_reladdr(op=>optag)
break
//
// Code tags
//
is CODETAG_GROUP
emit_ctag(op=>optag)
break
otherwise
return
wend
lcl_pending = lcl_pending=>opnext;
//
// Free this op
//
op=>opnext = freeop_lst
freeop_lst = op
loop
end
//
// Emit a sequence of ops (into the pending sequence)
//
def emit_seq(seq)#0
word op
byte string
string = FALSE
op = seq
while op
if op->opgroup == CONSTR_GROUP; string = TRUE; break; fin
op = op=>opnext
loop
pending_seq = cat_seq(pending_seq, seq)
//
// The source code comments in the output are much more logical if we don't
// merge multiple sequences together. There's no value in doing this merging
// if we're not optimizing, and we optionally allow it to be prevented even
// when we are optimizing by specifing the -N (NO_COMBINE) flag.
//
// We must also force output if the sequence includes a CS opcode, as the
// associated 'constant' is only temporarily valid.
//
if not (outflags & (OPTIMIZE|OPTIMIZE2)) or (outflags & NO_COMBINE) or string
emit_pending_seq
fin
end
//
// Emit lambda function
//
def emit_lambdafunc(tag, namestr, cparms, lambda_seq)#0
emit_ctag(tag)
framesize = cparms * 2
emit_enter(cparms)
emit_seq(lambda_seq)
emit_pending_seq
emit_leave
end

486
src/toolsrc/codeopt.pla Normal file
View File

@ -0,0 +1,486 @@
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
byte crunched, freeops, 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
freeops = 1
break
fin
if nextop->opcode == SUB_CODE
op->opcode = DEC_CODE
freeops = 1
break
fin
if nextop->opcode == SHL_CODE
op->opcode = DUP_CODE
nextop->opcode = ADD_CODE
crunched = 1
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 LB_CODE // BPTR_CODE
op=>opoffset = op=>opval
op->opcode = LAB_CODE
op->opgroup = GLOBAL_GROUP
freeops = 1
break
is LW_CODE // WPTR_CODE
op=>opoffset = op=>opval
op->opcode = LAW_CODE
op->opgroup = LOCAL_GROUP
freeops = 1
break
is SB_CODE
op=>opoffset = op=>opval
op->opcode = SAB_CODE
op->opgroup = GLOBAL_GROUP
freeops = 1
break
is SW_CODE
op=>opoffset = op=>opval
op->opcode = SAW_CODE
op->opgroup = GLOBAL_GROUP
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 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
freeops = 1
fin
break
is CONST_CODE // Collapse constant operation
nextopnext = nextop->nextop
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
is LOGIC_OR_CODE
op=>opval = op=>opval or nextop=>opval
freeops = 2
break
is LOGIC_AND_CODE
op=>opval = op=>opval and nextop=>opval
freeops = 2
break
wend // End of collapse constant operation
fin
if pass > 0 and freeops == 0 and op=>opval
crunched = try_dupify(op)
fin
break // CONST_CODE
is MUL_CODE
for shiftcnt = 0 to 15
if op=>opval == 1 << shiftcnt
op=>opval = shiftcnt
nextop->opcode = SHL_CODE
nextop->opgroup = STACK_GROUP
break
fin
next
break
is DIV_CODE
for shiftcnt = 0 to 15
if op=>opval == 1 << shiftcnt
op=>opval = shiftcnt
nextop->opcode = SHR_CODE
nextop->opcode = STACK_GROUP
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
is 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
op->opgroup = LOCAL_GROUP
freeops = 1
break
is LW_CODE
op->opcode = LLW_CODE
op->opgroup = LOCAL_GROUP
freeops = 1
break
is SB_CODE
op->opcode = SLB_CODE
op->opgroup = LOCAL_GROUP
freeops = 1
break
is SW_CODE
op->opcode = SLW_CODE
op->opgroup = LOCAL_GROUP
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
is 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
op->opgroup = GLOBAL_GROUP
freeops = 1
break
is LW_CODE
op->opcode = LAW_CODE
op->opgroup = GLOBAL_GROUP
freeops = 1
break
is SB_CODE
op->opcode = SAB_CODE
op->opgroup = GLOBAL_GROUP
freeops = 1
break
is SW_CODE
op->opcode = SAW_CODE
op->opgroup = GLOBAL_GROUP
freeops = 1
break
is ICAL_CODE
op->opcode = CALL_CODE
op->opgroup = GLOBAL_GROUP
freeops = 1
break
wend
if pass > 0 and not freeops
crunched = try_dupify(op)
fin
break // GADDR_CODE
is LLB_CODE
if pass > 0
crunched = try_dupify(op)
fin
break // LLB_CODE
is LLW_CODE
// LLW [n]:CB 8:SHR -> LLB [n+1]
if nextop->opcode == CONST_CODE and nextop=>opval == 8
if nextop=>opnext
nextopnext = nextop=>opnext
if nextopnext->opcode == SHR_CODE
op->opcode = LLB_CODE
op=>opoffset++
freeops = 2
break
fin
fin
fin
if pass and not freeops
crunched = try_dupify(op)
fin
break // LLW_CODE
is LAB_CODE
if pass and not is_hardware_address(op=>opoffset)
crunched = try_dupify(op)
fin
break // LAB_CODE
is LAW_CODE
// LAW x:CB 8:SHR -> LAB x+1
if nextop->opcode == CONST_CODE and nextop=>opval == 8
if nextop=>opnext
nextopnext = nextop=>opnext
if nextopnext->opcode == SHR_CODE
op->opcode = LAB_CODE
op=>opoffset++
freeops = 2
break
fin
fin
fin
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 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 > 0
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

91
src/toolsrc/codeseq.plh Normal file
View File

@ -0,0 +1,91 @@
//
// Constant code group
//
const CONST_GROUP = $00
const CONST_CODE = $2C
const CONSTR_GROUP = $01
const CONSTR_CODE = $2E
//
// Stack code group
//
const STACK_GROUP = $02
const INDEXB_CODE = $02
const ADD_CODE = $02
const SUB_CODE = $04
const MUL_CODE = $06
const DIV_CODE = $08
const MOD_CODE = $0A
const INC_CODE = $0C
const DEC_CODE = $0E
const NEG_CODE = $10
const COMP_CODE = $12
const AND_CODE = $14
const OR_CODE = $16
const EOR_CODE = $18
const SHL_CODE = $1A
const SHR_CODE = $1C
const INDEXW_CODE = $1E
const LOGIC_NOT_CODE = $20
const LOGIC_OR_CODE = $22
const LOGIC_AND_CODE = $24
const DROP_CODE = $30
const DUP_CODE = $32
const EQ_CODE = $40
const NE_CODE = $42
const GT_CODE = $44
const LT_CODE = $46
const GE_CODE = $48
const LE_CODE = $4A
const ICAL_CODE = $56
const RET_CODE = $5C
const LB_CODE = $60
const BPTR_CODE = $60
const LW_CODE = $62
const WPTR_CODE = $62
const SB_CODE = $70
const SW_CODE = $72
//
// Local address code group
//
const LOCAL_GROUP = $03
const LADDR_CODE = $28
const LLB_CODE = $64
const LLW_CODE = $66
const DLB_CODE = $6C
const DLW_CODE = $6E
const SLB_CODE = $74
const SLW_CODE = $76
//
// Global address code group
//
const GLOBAL_GROUP = $04
const GADDR_CODE = $26
const LAB_CODE = $68
const LAW_CODE = $6A
const SAB_CODE = $78
const SAW_CODE = $7A
const DAB_CODE = $7C
const DAW_CODE = $7E
const CALL_CODE = $54
//
// Relative address code group
//
const RELATIVE_GROUP = $05
const BRFALSE_CODE = $4C
const BRTRUE_CODE = $4E
const BRNCH_CODE = $50
//
// Code tag address group
//
const CODETAG_GROUP = $06
//
// Code sequence op
//
struc t_opseq
byte opcode
byte opgroup
word opval[]
word optag
word opoffset
word opnext
end

View File

@ -202,96 +202,13 @@ byte[16] sizestack
byte[16] typestack
word valsp
//
// Constant code group
// Code sequence shared with optimizer
//
const CONST_GROUP = $00
const CONST_CODE = $2C
const CONSTR_GROUP = $01
const CONSTR_CODE = $2E
include "toolsrc/codeseq.plh"
//
// Stack code group
//
const STACK_GROUP = $02
const INDEXB_CODE = $02
const ADD_CODE = $02
const SUB_CODE = $04
const MUL_CODE = $06
const DIV_CODE = $08
const MOD_CODE = $0A
const INC_CODE = $0C
const DEC_CODE = $0E
const NEG_CODE = $10
const COMP_CODE = $12
const AND_CODE = $14
const OR_CODE = $16
const EOR_CODE = $18
const SHL_CODE = $1A
const SHR_CODE = $1C
const INDEXW_CODE = $1E
const LOGIC_NOT_CODE = $20
const LOGIC_OR_CODE = $22
const LOGIC_AND_CODE = $24
const DROP_CODE = $30
const DUP_CODE = $32
const EQ_CODE = $40
const NE_CODE = $42
const GT_CODE = $44
const LT_CODE = $46
const GE_CODE = $48
const LE_CODE = $4A
const ICAL_CODE = $56
const RET_CODE = $5C
const LB_CODE = $60
const BPTR_CODE = $60
const LW_CODE = $62
const WPTR_CODE = $62
const SB_CODE = $70
const SW_CODE = $72
//
// Local address code group
//
const LOCAL_GROUP = $03
const LADDR_CODE = $28
const LLB_CODE = $64
const LLW_CODE = $66
const DLB_CODE = $6C
const DLW_CODE = $6E
const SLB_CODE = $74
const SLW_CODE = $76
//
// Global address code group
//
const GLOBAL_GROUP = $04
const GADDR_CODE = $26
const LAB_CODE = $68
const LAW_CODE = $6A
const SAB_CODE = $78
const SAW_CODE = $7A
const DAB_CODE = $7C
const DAW_CODE = $7E
const CALL_CODE = $54
//
// Relative address code group
//
const RELATIVE_GROUP = $05
const BRFALSE_CODE = $4C
const BRTRUE_CODE = $4E
const BRNCH_CODE = $50
//
// Code tag address group
//
const CODETAG_GROUP = $06
//
// Symbol table variables
//
struc t_opseq
byte opcode
byte opgroup
word opval[]
word optag
word opoffset
word opnext
end
struc t_id
word idval
word idtype
@ -312,7 +229,7 @@ const IDGLOBALSZ = 2048
const IDLOCALSZ = 512
word codetag = -1
word idglobal_tbl, idlocal_tbl, ctag_tbl
word freeop_lst, pending_seq
word pending_seq
word globals, lastglobal, lastlocal, savelast
word codebufsz, datasize, framesize, savesize
byte locals, savelocals
@ -322,6 +239,11 @@ byte[16] moddep_tbl[8]
byte moddep_cnt
predef emit_pending_seq#0
//
// Exports for optimizer module
//
export word freeop_lst
export word optimize_seq
//
// Compiler flags
//
const OPTIMIZE = 1
@ -467,6 +389,8 @@ end
// Include code to reduce size of this file
//
include "toolsrc/codegen.pla"
//include "toolsrc/codeopt.pla"
//include "toolsrc/writerel.pla"
include "toolsrc/lex.pla"
include "toolsrc/parse.pla"
//
@ -474,10 +398,17 @@ include "toolsrc/parse.pla"
//
arg = argNext(argFirst)
if ^arg and ^(arg + 1) == '-'
opt = arg + 2
opt = arg + 2
while TRUE
if toupper(^opt) == 'O'
outflags = outflags | OPTIMIZE
//
// Load optimizer module here
//
if modexec("CODEOPT") >= 0
outflags = outflags | OPTIMIZE
else
puts("\nOptimizer disabled\n")
fin
opt++
if ^opt == '2'
outflags = outflags | OPTIMIZE2
@ -519,7 +450,7 @@ if srcfile and relfile
//
parse_module
fileio:close(srcref)
puts("Bytes compiled: "); puti(codeptr - codebuff); putln
puts("\nBytes compiled: "); puti(codeptr - codebuff); putln
//
// Write REL file
//
@ -530,13 +461,13 @@ if srcfile and relfile
//writerel(srcref)
fileio:close(srcref)
else
puts("Error opening: "); puts(@relfile); putln
puts("\nError opening: "); puts(@relfile); putln
fin
fin
else
puts("Error opening: "); puts(@srcfile); putln
puts("\nError opening: "); puts(@srcfile); putln
fin
else
puts("Usage: +PLASM [-[W][O[2]][N]] <src> <rel>\n")
puts("Usage:+PLASM [-[W][O[2]][N]] <src> <rel>\n")
fin
done