diff --git a/src/makefile b/src/makefile index 75ca097..a767e77 100755 --- a/src/makefile +++ b/src/makefile @@ -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 # diff --git a/src/toolsrc/codegen.pla b/src/toolsrc/codegen.pla index eeeb5cc..17172d9 100644 --- a/src/toolsrc/codegen.pla +++ b/src/toolsrc/codegen.pla @@ -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 diff --git a/src/toolsrc/codeopt.pla b/src/toolsrc/codeopt.pla new file mode 100644 index 0000000..4feea6f --- /dev/null +++ b/src/toolsrc/codeopt.pla @@ -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 diff --git a/src/toolsrc/codeseq.plh b/src/toolsrc/codeseq.plh new file mode 100644 index 0000000..c0e4a61 --- /dev/null +++ b/src/toolsrc/codeseq.plh @@ -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 diff --git a/src/toolsrc/plasm.pla b/src/toolsrc/plasm.pla index aae0835..19f578f 100644 --- a/src/toolsrc/plasm.pla +++ b/src/toolsrc/plasm.pla @@ -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]] \n") + puts("Usage:+PLASM [-[W][O[2]][N]] \n") fin done