1
0
mirror of https://github.com/dschmenk/PLASMA.git synced 2025-04-21 20:37:40 +00:00

PLASMA PLASMA compiler roughly up-to-date with PLASMA C compiler

This commit is contained in:
David Schmenk 2017-12-28 07:24:44 -08:00
parent 7843bc41ce
commit e3cd834d1d
2 changed files with 491 additions and 99 deletions

View File

@ -29,64 +29,52 @@ def idmatch(nameptr, len, idptr, idcnt)
while idcnt
if len == idptr->idname
for i = 1 to len
if nameptr->[i - 1] <> idptr->idname.[i]
break
fin
if nameptr->[i - 1] <> idptr->idname.[i]; break; fin
next
if i > len
return idptr
fin
if i > len; return idptr; fin
fin
idptr = idptr + idptr->idname + idrecsz
idptr = idptr + idptr->idname + t_id
idcnt--
loop
return 0
return NULL
end
def id_lookup(nameptr, len)
word idptr
idptr = idmatch(nameptr, len, idlocal_tbl, locals)
if idptr
return idptr
if not idptr
idptr = idmatch(nameptr, len, idglobal_tbl, globals)
if not idptr; exit_err(@undecl_id); fin
fin
idptr = idmatch(nameptr, len, idglobal_tbl, globals)
if idptr
return idptr
fin
exit_err(@undecl_id)
return 0
return idptr
end
def idglobal_lookup(nameptr, len)
return idmatch(nameptr, len, idglobal_tbl, globals)
word idptr
idptr idmatch(nameptr, len, idglobal_tbl, globals)
if not idptr; exit_err(@undecl_id); fin
return idptr
end
def idlocal_add(namestr, len, type, size)
if idmatch(namestr, len, @idlocal_tbl, locals); return exit_err(@dup_id); fin
if idmatch(namestr, len, @idlocal_tbl, locals); exit_err(@dup_id); fin
lastlocal=>idval = framesize
lastlocal->idtype = type | LOCAL_TYPE
nametostr(namestr, len, lastlocal + idname)
locals++
lastlocal = lastlocal + idrecsz + len
if lastlocal > idlocal_tbl + idlocal_tblsz
exit_err(@local_sym_overflw)
fin
if lastlocal > idlocal_tbl + idlocal_tblsz; exit_err(@local_sym_overflw); fin
framesize = framesize + size
if framesize > 255
return exit_err(@local_overflw)
fin
if framesize > 255; exit_err(@local_overflw); fin
return TRUE
end
def iddata_add(namestr, len, type, size)
if idmatch(namestr, len, idglobal_tbl, globals); return exit_err(@dup_id); fin
if idmatch(namestr, len, idglobal_tbl, globals); exit_err(@dup_id); fin
lastglobal=>idval = datasize
lastglobal->idtype = type
nametostr(namestr, len, lastglobal + idname)
emit_iddata(datasize, size, lastglobal + idname)
globals++
lastglobal = lastglobal + idrecsz + len
if lastglobal > idglobal_tbl + idglobal_tblsz
prstr(@global_sym_overflw)
exit
fin
if lastglobal > idglobal_tbl + idglobal_tblsz; exit_err((@global_sym_overflw); fin
datasize = datasize + size
return TRUE
end
@ -99,7 +87,7 @@ def iddata_size(type, varsize, initsize)#0
fin
end
def idglobal_add(namestr, len, type, value)
if idmatch(namestr, len, idglobal_tbl, globals); return exit_err(@dup_id); fin
if idmatch(namestr, len, idglobal_tbl, globals); exit_err(@dup_id); fin
lastglobal=>idval = value
lastglobal->idtype = type
nametostr(namestr, len, lastglobal + idname)
@ -150,7 +138,7 @@ end
// Flags are:
//
def ctag_new
if codetag >= ctag_max; return exit_err(@ctag_full); fin
if codetag >= ctag_max; exit_err(@ctag_full); fin
codetag = codetag + 1
ctag_tbl:[codetag] = 0 // Unresolved, nothing to update yet
return codetag | IS_CTAG
@ -159,7 +147,7 @@ def ctag_resolve(ctag)#0
word updtptr, nextptr
ctag = ctag & MASK_CTAG // Better be a ctag!
if ctag_tbl:[ctag] & IS_RESOLVED;exit_err(@dup_id); return; fin
if ctag_tbl:[ctag] & IS_RESOLVED;exit_err(@dup_id); fin
updtptr = ctag_tbl:[ctag] & MASK_CTAG
while updtptr
//
@ -392,11 +380,10 @@ end
def emit_indexword#0
emit_op($1E)
end
def emit_unaryop(op)
def emit_unaryop(op)#0
when op
is NEG_TKN
emit_op($10); break
is ALT_COMP_TKN
is COMP_TKN
emit_op($12); break
is LOGIC_NOT_TKN
@ -410,60 +397,21 @@ def emit_unaryop(op)
is WPTR_TKN
emit_op($62); break
otherwise
return FALSE
exit_err("Invalid unary operation")
wend
return TRUE
end
def emit_binaryop(op)
def emit_binaryop(op)#0
when op
is MUL_TKN
//
// Replace MUL 2 with SHL 1
//
if lastop == $2A and ^(codeptr - 1) == 2 // CB 2
codeptr = codeptr - 1
emit_byte(1) // CB 1
emit_op($1A) // SHL
else
emit_op($06)
fin
break
emit_op($06); break
is DIV_TKN
//
// Replace DIV 2 with SHR 1
//
if lastop == $2A and ^(codeptr - 1) == 2 // CB 2
codeptr = codeptr - 1
emit_byte(1) // CB 1
emit_op($1C) // SHR
else
emit_op($08)
fin
break
emit_op($08); break
is MOD_TKN
emit_op($0A); break
is ADD_TKN
//
// Replace ADD 1 with INCR
//
if lastop == $2A and ^(codeptr - 1) == 1 // CB 1
codeptr = codeptr - 2
emit_op($0C) // INC_OP
else
emit_op($02)
fin
break
emit_op($02); break
is SUB_TKN
//
// Replace SUB 1 with DECR
//
if lastop == $2A and ^(codeptr - 1) == 1 // CB 1
codeptr = codeptr - 2
emit_op($0E) // DEC_OP
else
emit_op($04)
fin
break
emit_op($04); break
is SHL_TKN
emit_op($1A); break
is SHR_TKN
@ -491,9 +439,8 @@ def emit_binaryop(op)
is LOGIC_AND_TKN
emit_op($24); break
otherwise
return FALSE
exit_err("Invalid operation")
wend
return TRUE
end
def emit_brtru(tag)#0
emit_op($4E)
@ -540,7 +487,7 @@ def emit_enter(cparams)#0
fin
end
//
//New/release sequence ops
// New/release sequence ops
//
def new_op
word op
@ -573,7 +520,448 @@ def release_seq(seq)#0
loop
end
//
//Generate a sequence of code
// 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 opnext
crunched = 0
opnext = op=>nextop
while opnext
if op->code <> opn->code
return crunched
when op->code
is CONST_CODE
if op->val <> opnext->val; return crunched; fin
break
is LADDR_CODE
is LLB_CODE
is LLW_CODE
if op=>offsz <> opnext=>offsz; return crunched; fin
break
is GADDR_CODE
is LAB_CODE
is LAW_CODE
if (op=>tag <> opnext=>tag) or (op=>offsz <> opnext=>offsz) or (op->type <> opnext->type); return crunched; fin
break
otherwise
return crunched
wend
opnext->code = DUP_CODE
opnext = opnext=>nextop
crunched = 1
loop
return crunched
end
//
// Crunch sequence (peephole optimize)
//
def crunch_seq(seq, pass)
word opnext opnextnext opprev, op
byte crunched, freeops, shiftcnt
opprev = NULL
op = *seq
opnext = op=>nextop
crunched = FALSE
freeops = 0
while op and opnext
when op->code
is CONST_CODE
if op=>val == 1
{
if opnext->code == BINARY_CODE|ADD_TOKEN
op->code = INC_CODE
freeops = 1
break
fin
if opnext->code == BINARY_CODE|SUB_TOKEN
op->code = DEC_CODE
freeops = 1
break
fin
if opnext->code == BINARY_CODE|SHL_TOKEN
op->code = DUP_CODE
opnext->code = BINARY_CODE|ADD_TOKEN
crunched = 1
break
fin
fin
when opnext->code
is NEG_CODE
op=>val = -(op=>val)
freeops = 1
break
is COMP_CODE
op->val = ~(op=>val)
freeops = 1
break
is LOGIC_NOT_CODE
op=>val = op=>val ?? FALSE :: TRUE
freeops = 1
break
is UNARY_CODE|BPTR_TOKEN
is LB_CODE
op=>offsz = op=>val
op->code = LAB_CODE
freeops = 1
break
is UNARY_CODE|WPTR_TOKEN
is LW_CODE
op=>offsz = op=>val
op->code = LAW_CODE
freeops = 1
break
is SB_CODE
op=>offsz = op=>val
op->code = SAB_CODE
freeops = 1
break
is SW_CODE
op=>offsz = op=>val
op->code = SAW_CODE
freeops = 1
break
is BRFALSE_CODE
if op=>val
freeops = -2 // Remove constant and never taken branch
else
op->code = BRNCH_CODE // Always taken branch
op=>tag = opnext=>tag
freeops = 1
fin
break
is BRTRUE_CODE
if not op=>val
freeops = -2 // Remove constant never taken branch
else
op->code = BRNCH_CODE // Always taken branch
op=>tag = opnext=>tag
freeops = 1
fin
break
is NE_CODE
if not op=>val
freeops = -2 // Remove ZERO:ISNE
fin
break
is EQ_CODE
if not op=>val
op->code = LOGIC_NOT_CODE
freeops = 1
fin
break
is CONST_CODE // Collapse constant operation
opnextnext = opnext->nextop
if opnextnext
when opnextnext->code
is BINARY_CODE|MUL_TOKEN
op=>val = op=>val * opnext=>val
freeops = 2
break
is BINARY_CODE|DIV_TOKEN
op=>val = op=>val / opnext=>val
freeops = 2
break
is BINARY_CODE|MOD_TOKEN
op=>val = op=>val % opnext=>val
freeop = 2
break
is BINARY_CODE|ADD_TOKEN
op=>val = op=>val + opnext=>val
freeops = 2
break
is BINARY_CODE|SUB_TOKEN
op=>val = op=>val - opnext=>val
freeops = 2
break
is BINARY_CODE|SHL_TOKEN
op=>val = op=>val << opnext=>val
freeops = 2
break
is BINARY_CODE|SHR_TOKEN
op=>val = op=>val >> opnext=>val
freeops = 2
break
is BINARY_CODE|AND_TOKEN
op=>val = op=>val & opnext=>val
freeops = 2
break
is BINARY_CODE|OR_TOKEN
op=>val = op=>val | opnext=>val
freeops = 2
break
is BINARY_CODE|EOR_TOKEN
op=>val = op=>val ^ opnext=>val
freeops = 2
break
is BINARY_CODE|EQ_TOKEN
op=>val = op=>val == opnext=>val
freeops = 2
break
is BINARY_CODE|NE_TOKEN
op=>val = op=>val <> opnext=>val
freeops = 2
break
is BINARY_CODE|GE_TOKEN
op=>val = op=>val >= opnext=>val
freeops = 2
break
is BINARY_CODE|LT_TOKEN
op=>val = op=>val < opnext=>val
freeops = 2
break
is BINARY_CODE|GT_TOKEN
op=>val = op=>val > opnext=>val
freeops = 2
break
is BINARY_CODE|LE_TOKEN
op=>val = op=>val <= opnext=>val
freeops = 2
break
is BINARY_CODE|LOGIC_OR_TOKEN
op=>val = op=>val or opnext=>val
freeops = 2
break
is BINARY_CODE|LOGIC_AND_TOKEN
op=>val = op=>val and opnext=>val
freeops = 2
break
wend // End of collapse constant operation
if pass > 0 and freeops == 0 and op=>val
crunched = try_dupify(op)
fin
break // CONST_CODE
is BINARY_CODE|MUL_TOKEN
for shiftcnt = 0 to 15
if op=>val == 1 << shiftcnt
op=>val = shiftcnt
opnext->code = BINARY_CODE|SHL_TOKEN
break
fin
next
break
is BINARY_CODE|DIV_TOKEN
for shiftcnt = 0 to 15
if op=>val == 1 << shiftcnt
op=>val = shiftcnt
opnext->code = BINARY_CODE|SHR_TOKEN
break
fin
next
break
}
break // CONST_CODE
is LADDR_CODE
when opnext->code
is CONST_CODE
if opnext=>nextop
opnextnext = opnext=>nextop
when opnextnext->code
is ADD_CODE
is INDEXB_CODE
op=>offsz = op=>offsz + opnext=>val
freeops = 2
break
is INDEXW_CODE
op=>offsz = op=>offsz + opnext=>val * 2
freeops = 2
break
wend
fin
break
is LB_CODE
op->code = LLB_CODE
freeops = 1
break
is LW_CODE
op->code = LLW_CODE
freeops = 1
break
is SB_CODE
op->code = SLB_CODE
freeops = 1
break
is SW_CODE
op->code = SLW_CODE
freeops = 1
break
wend
if pass > 0 and not freeops
crunched = try_dupify(op)
fin
break // LADDR_CODE
is GADDR_CODE
when opnext->code
is CONST_CODE
if opnext=>nextop
opnextnext = opnext=>nextop
when opnextnext->code
is ADD_CODE
is INDEXB_CODE
op=>offsz = op=>offsz + opnext=>val
freeops = 2
break
is INDEXW_CODE
op=>offsz = op=>offsz + opnext=>val * 2
freeops = 2
break
wend
fin
break
is LB_CODE:
op->code = LAB_CODE
freeops = 1
break
is LW_CODE
op->code = LAW_CODE
freeops = 1
break
is SB_CODE
op->code = SAB_CODE
freeops = 1
break
is SW_CODE
op->code = SAW_CODE
freeops = 1
break
is ICAL_CODE
op->code = CALL_CODE
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)
break // LLB_CODE
is LLW_CODE
// LLW [n]:CB 8:SHR -> LLB [n+1]
if opnext->code == CONST_CODE and opnext=>val == 8
if opnext=>nextop
opnextnext = opnext=>nextop
if opnextnext->code == SHR_CODE
op->code = LLB_CODE
op=>offsz++
freeops = 2
break
fin
fin
fin
if pass > 0 and not freeops
crunched = try_dupify(op)
fin
break // LLW_CODE
is LAB_CODE
if pass > 0 and (op->type) // || !is_hardware_address(op->offsz)))
crunched = try_dupify(op)
break // LAB_CODE
is LAW_CODE
// LAW x:CB 8:SHR -> LAB x+1
if opnext->code == CONST_CODE and opnext=>val == 8
if opnext=>nextop
opnextnext = opnext=>nextop
if opnextnext->code == SHR_CODE
op->code = LAB_CODE
op=>offsz++
freeops = 2
break
fin
fin
fin
if pass > 0 and not freeops and (op->type) // || !is_hardware_address(op->offsz)))
crunched = try_dupify(op)
fin
break // LAW_CODE
is LOGIC_NOT_CODE
when opnext->code
is BRFALSE_CODE
op->code = BRTRUE_CODE
op=>tag = opnext=>tag
freeops = 1
break
is BRTRUE_CODE
op->code = BRFALSE_CODE
op=>tag = opnext=>tag
freeops = 1
break
wend
break // LOGIC_NOT_CODE
is SLB_CODE
if opnext->code == LLB_CODE and op=>offsz == opnext=>offsz
op->code = DLB_CODE
freeops = 1
fin
break // SLB_CODE
is SLW_CODE
if opnext->code == LLW_CODE and op=>offsz == opnext=>offsz
op->code = DLW_CODE
freeops = 1
fin
break // SLW_CODE
is SAB_CODE
if opnext->code == LAB_CODE and op=>tag == opnext=>tag and op=>offsz == opnext=>offsz and op->type == opnext->type
op->code = DAB_CODE
freeops = 1
fin
break // SAB_CODE
is SAW_CODE
if opnext->code == LAW_CODE and op=>tag == opnext=>tag and op=>offsz == opnext=>offsz and op->type == opnext->type
op->code = 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
opnext = op=>nextop
release_op(op)
*seq = opnext
op = opnext
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
opnext = op=>nextop
fin
fin
while freeops
op=>nextop = opnext=>nextop
opnext=>nextop = freeop_lst
freeop_lst = opnext
opnext = op=>nextop
crunched = TRUE
freeops--
loop
opprev = op
op = opnext
opnext = op=>nextop
loop
return crunched
end
//
// Generate a sequence of code
//
def gen_seq(seq, opcode, cval, tag, offsz, type)
{
@ -596,7 +984,7 @@ def gen_seq(seq, opcode, cval, tag, offsz, type)
return seq
end
//
//Append one sequence to the end of another
// Append one sequence to the end of another
//
def cat_seq(seq1, seq2)
{
@ -609,10 +997,11 @@ def cat_seq(seq1, seq2)
return seq1
fin
//
//Emit the pending sequence
// 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
@ -620,6 +1009,7 @@ def emit_pending_seq#0
// 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
@ -661,7 +1051,7 @@ def emit_pending_seq#0
break
is SW_CODE
emit_sw()
break;
break
is SLB_CODE
emit_slb(op-=>offsz)
break
@ -727,7 +1117,7 @@ def emit_pending_seq#0
break
is CODETAG_CODE
printf("_B%03d%c\n", op->tag, LBL);
break;
break
is NEG_CODE
is COMP_CODE
is LOGIC_NOT_CODE
@ -811,6 +1201,7 @@ def emit_seq(seq)#0
op = op=>nextop
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
@ -818,6 +1209,8 @@ def emit_seq(seq)#0
//
// We must also force output if the sequence includes a CS opcode, as the
// associated 'constant' is only temporarily valid.
if !(outflags & (OPTIMIZE|OPTIMIZE2)) or (outflags & NO_COMBINE) or string
//
if not (outflags & (OPTIMIZE|OPTIMIZE2)) or (outflags & NO_COMBINE) or string
emit_pending_seq
fin
end

View File

@ -216,9 +216,9 @@ end
// Normal expression parsing
//
def parse_list#2
{
byte listdepth, stackdepth
word listseq
listseq = NULL
listdepth = 0
repeat
@ -226,7 +226,7 @@ def parse_list#2
listdepth = listdepth + stackdepth
until not listseq or token <> COMMA_TOKEN
return listseq, listdepth
}
end
def parse_value(codeseq, rvalue)#2
byte cfnparms, cfnvals, stackdepth, deref, type, operation
word optos, idptr, value, const_offset
@ -435,7 +435,7 @@ def parse_value(codeseq, rvalue)#2
wend
until not operation
//
//Resolve outstanding dereference pointer loads
// Resolve outstanding dereference pointer loads
//
while deref > rvalue
deref--
@ -461,11 +461,11 @@ def parse_value(codeseq, rvalue)#2
fin
fin
//
//Output pre-operations
// Output pre-operations
//
valseq = cat_seq(valseq, uopseq)
//
//Wrap up LVALUE store
// Wrap up LVALUE store
//
if not rvalue
stackdepth--
@ -514,7 +514,7 @@ def parse_expr(codeseq)#2
stackdepth--
loop
//
//Look for ternary operator
// Look for ternary operator
//
if token == TERNARY_TOKEN
if stackdepth <> 1; exit_err("Ternary op must evaluate to single value"); fin
@ -1068,11 +1068,10 @@ def parse_mods
if token <> END_TKN; exit_err("Missing IMPORT/END"); fin
scan
fin
if token == EOL_TOKEN
return TRUE
if token == EOL_TOKEN; return TRUE; fin
emit_moddep(0, 0)
return FALSE
}
end
def parse_lambda
word func_tag
byte cfnparms
@ -1080,7 +1079,7 @@ def parse_lambda
if not infunc; exit_err("Lambda functions only allowed inside definitions"); fin
idlocal_save
//
//Parse parameters and return value count
// Parse parameters and return value count
//
cfnparms = 0
if scan == OPEN_PAREN_TKN
@ -1216,8 +1215,8 @@ def parse_module
loop
framesize = 0
entrypoint = codeptr
prevstmnt = 0
emit_enter(0)
prevstmnt = 0
if token <> DONE_TKN
while parse_stmnt
nextln