mirror of
https://github.com/dschmenk/PLASMA.git
synced 2025-04-21 04:39:48 +00:00
Modular optimizer
This commit is contained in:
parent
ead253ef14
commit
a8f7149bb7
@ -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
|
||||
#
|
||||
|
@ -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
486
src/toolsrc/codeopt.pla
Normal 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
91
src/toolsrc/codeseq.plh
Normal 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
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user