mirror of
https://github.com/irmen/prog8.git
synced 2025-03-31 01:32:15 +00:00
defers are now only registered/called when flow of control actually reached the defer statement
a defer statement sets its corresponding bit in a bitmask that is shifted in the defer handler routine to see what defer blocks to call.
This commit is contained in:
parent
0247fb0d84
commit
a6159702da
@ -129,7 +129,6 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"callfar2" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("argA", arrayOf(DataType.UBYTE)), FParam("argX", arrayOf(DataType.UBYTE)), FParam("argY", arrayOf(DataType.UBYTE)), FParam("argC", arrayOf(DataType.BOOL))), DataType.UWORD),
|
||||
"call" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"invoke_defer" to FSignature(false, emptyList(), null),
|
||||
)
|
||||
|
||||
val InplaceModifyingBuiltinFunctions = setOf(
|
||||
|
@ -9,7 +9,6 @@ import kotlin.io.path.readText
|
||||
|
||||
|
||||
const val internedStringsModuleName = "prog8_interned_strings"
|
||||
const val deferLabel = "prog8_defer_statements"
|
||||
|
||||
|
||||
/**
|
||||
|
@ -603,11 +603,11 @@ class AsmGen6502Internal (
|
||||
is PtRepeatLoop -> translate(stmt)
|
||||
is PtWhen -> translate(stmt)
|
||||
is PtIncludeBinary -> translate(stmt)
|
||||
is PtBreakpoint -> translate(stmt)
|
||||
is PtBreakpoint -> translateBrk()
|
||||
is PtVariable, is PtConstant, is PtMemMapped -> { /* do nothing; variables are handled elsewhere */ }
|
||||
is PtBlock -> throw AssemblyError("block should have been handled elsewhere")
|
||||
is PtDefer -> throw AssemblyError("defer should have been transformed")
|
||||
is PtNodeGroup -> stmt.children.forEach { translate(it) }
|
||||
is PtDefer -> translate(stmt)
|
||||
is PtNop -> {}
|
||||
else -> throw AssemblyError("missing asm translation for $stmt")
|
||||
}
|
||||
@ -1084,7 +1084,7 @@ $repeatLabel""")
|
||||
out(" .binary \"$pathForAssembler\" $offset $length")
|
||||
}
|
||||
|
||||
private fun translate(brk: PtBreakpoint) {
|
||||
private fun translateBrk() {
|
||||
val label = "_prog8_breakpoint_${breakpointLabels.size+1}"
|
||||
breakpointLabels.add(label)
|
||||
out(label)
|
||||
@ -1093,15 +1093,6 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(defer: PtDefer) {
|
||||
val sub = defer.definingSub()!!
|
||||
out("${sub.name}.$deferLabel")
|
||||
for(stmt in defer.children) {
|
||||
translate(stmt)
|
||||
}
|
||||
out(" rts")
|
||||
}
|
||||
|
||||
internal fun signExtendAYlsb(valueDt: DataType) {
|
||||
// sign extend signed byte in A to full word in AY
|
||||
when(valueDt) {
|
||||
|
@ -69,18 +69,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
|
||||
"prog8_lib_square_byte" -> funcSquare(fcall, DataType.UBYTE, resultRegister)
|
||||
"prog8_lib_square_word" -> funcSquare(fcall, DataType.UWORD, resultRegister)
|
||||
"invoke_defer" -> funcInvokeDefer(fcall)
|
||||
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
|
||||
}
|
||||
|
||||
return BuiltinFunctions.getValue(fcall.name).returnType
|
||||
}
|
||||
|
||||
private fun funcInvokeDefer(call: PtBuiltinFunctionCall) {
|
||||
val sub = call.definingSub()!!
|
||||
asmgen.out(" jsr ${sub.name}.$deferLabel")
|
||||
}
|
||||
|
||||
private fun funcSquare(fcall: PtBuiltinFunctionCall, resultType: DataType, resultRegister: RegisterOrPair?) {
|
||||
// square of word value is faster with dedicated routine, square of byte just use the regular multiplication routine.
|
||||
when (resultType) {
|
||||
|
@ -44,19 +44,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(call)
|
||||
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
|
||||
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
|
||||
"invoke_defer" -> funcInvokeDefer(call)
|
||||
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcInvokeDefer(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val sub = call.definingSub()!!
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.CALL, labelSymbol = "${sub.name}.$deferLabel",
|
||||
fcallArgs = FunctionCallArgs(emptyList(), emptyList())), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
|
||||
private fun funcSquare(call: PtBuiltinFunctionCall, resultType: IRDataType): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val valueTr = exprGen.translateExpression(call.args[0])
|
||||
|
@ -235,7 +235,6 @@ class IRCodeGen(
|
||||
listOf(chunk)
|
||||
}
|
||||
is PtConditionalBranch -> translate(node)
|
||||
is PtDefer -> translate(node)
|
||||
is PtInlineAssembly -> listOf(IRInlineAsmChunk(null, node.assembly, node.isIR, null))
|
||||
is PtIncludeBinary -> listOf(IRInlineBinaryChunk(null, readBinaryData(node), null))
|
||||
is PtAddressOf,
|
||||
@ -255,6 +254,7 @@ class IRCodeGen(
|
||||
is PtBool,
|
||||
is PtArray,
|
||||
is PtBlock,
|
||||
is PtDefer -> throw AssemblyError("defer should have been transformed")
|
||||
is PtString -> throw AssemblyError("should not occur as separate statement node ${node.position}")
|
||||
is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
|
||||
else -> TODO("missing codegen for $node")
|
||||
@ -273,16 +273,6 @@ class IRCodeGen(
|
||||
.map { it.toUByte() }
|
||||
}
|
||||
|
||||
private fun translate(defer: PtDefer): IRCodeChunks {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
for(stmt in defer.children) {
|
||||
result += translateNode(stmt)
|
||||
}
|
||||
addInstr(result, IRInstruction(Opcode.RETURN), null)
|
||||
val sub = defer.definingSub()!!
|
||||
return labelFirstChunk(result, "${sub.name}.$deferLabel")
|
||||
}
|
||||
|
||||
private fun translate(branch: PtConditionalBranch): IRCodeChunks {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
|
@ -6,12 +6,20 @@ import prog8.code.core.*
|
||||
|
||||
|
||||
internal fun postprocessIntermediateAst(program: PtProgram, st: SymbolTable, errors: IErrorReporter) {
|
||||
coalesceDefers(program)
|
||||
integrateDefers(program, st)
|
||||
processDefers(program, st, errors)
|
||||
}
|
||||
|
||||
|
||||
private fun coalesceDefers(program: PtProgram) {
|
||||
private fun processDefers(program: PtProgram, st: SymbolTable, errors: IErrorReporter) {
|
||||
val defers = setDeferMasks(program, errors)
|
||||
if(errors.noErrors())
|
||||
integrateDefers(defers, program, st)
|
||||
}
|
||||
|
||||
private const val maskVarName = "prog8_defers_mask"
|
||||
private const val invokeDefersRoutineName = "prog8_invoke_defers"
|
||||
|
||||
private fun setDeferMasks(program: PtProgram, errors: IErrorReporter): Map<PtSub, List<PtDefer>> {
|
||||
val defersPerSub = mutableMapOf<PtSub, MutableList<PtDefer>>().withDefault { mutableListOf() }
|
||||
|
||||
walkAst(program) { node, _ ->
|
||||
@ -24,58 +32,79 @@ private fun coalesceDefers(program: PtProgram) {
|
||||
}
|
||||
|
||||
for((sub, defers) in defersPerSub) {
|
||||
val coalescedDefer = PtDefer(sub.position)
|
||||
for(defer in defers.reversed()) {
|
||||
for(stmt in defer.children)
|
||||
coalescedDefer.add(stmt)
|
||||
sub.children.remove(defer)
|
||||
|
||||
if(defers.isEmpty())
|
||||
continue
|
||||
if (defers.size > 8) {
|
||||
errors.err("can have no more than 8 defers per subroutine", sub.position)
|
||||
return emptyMap()
|
||||
}
|
||||
|
||||
if(coalescedDefer.children.isNotEmpty()) {
|
||||
sub.add(coalescedDefer)
|
||||
// define the bitmask variable and set it to zero
|
||||
val deferVariable = PtVariable(maskVarName, DataType.UBYTE, ZeropageWish.NOT_IN_ZEROPAGE, null, null, sub.position)
|
||||
val assignZero = PtAssignment(sub.position)
|
||||
assignZero.add(PtAssignTarget(false, sub.position).also {
|
||||
it.add(PtIdentifier(sub.scopedName+"."+maskVarName, DataType.UBYTE, sub.position))
|
||||
})
|
||||
assignZero.add(PtNumber(DataType.UBYTE, 0.0, sub.position))
|
||||
sub.add(0, assignZero)
|
||||
sub.add(0, deferVariable)
|
||||
|
||||
for((deferIndex, defer) in defers.withIndex()) {
|
||||
// replace the defer statement with one that enables the bit in the mask for this defer
|
||||
val idx = defer.parent.children.indexOf(defer)
|
||||
val enableDefer = PtAugmentedAssign("|=", defer.position)
|
||||
val target = PtAssignTarget(true, defer.position)
|
||||
target.add(PtIdentifier(sub.scopedName+"."+maskVarName, DataType.UBYTE, defer.position))
|
||||
enableDefer.add(target)
|
||||
// enable the bit for this defer (beginning with high bits so the handler can simply shift right to check them in reverse order)
|
||||
enableDefer.add(PtNumber(DataType.UBYTE, (1 shl (defers.size-1 - deferIndex)).toDouble(), defer.position))
|
||||
enableDefer.parent = sub
|
||||
sub.children[idx] = enableDefer
|
||||
}
|
||||
}
|
||||
|
||||
return defersPerSub
|
||||
}
|
||||
|
||||
|
||||
private fun integrateDefers(program: PtProgram, st: SymbolTable) {
|
||||
val jumpsToAugment = mutableListOf<PtJump>()
|
||||
private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtProgram, st: SymbolTable) {
|
||||
val jumpsAndCallsToAugment = mutableListOf<PtNode>()
|
||||
val returnsToAugment = mutableListOf<PtReturn>()
|
||||
val subEndsToAugment = mutableListOf<PtSub>()
|
||||
val callsToAugment = mutableListOf<PtFunctionCall>()
|
||||
|
||||
walkAst(program) { node, _ ->
|
||||
when(node) {
|
||||
is PtFunctionCall -> {
|
||||
if(node.name.startsWith("sys.exit"))
|
||||
callsToAugment.add(node)
|
||||
}
|
||||
is PtJump -> {
|
||||
if(node.identifier!=null) {
|
||||
val stNode = st.lookup(node.identifier!!.name)!!
|
||||
val targetSub = stNode.astNode.definingSub()
|
||||
if(targetSub!=node.definingSub())
|
||||
jumpsToAugment.add(node)
|
||||
if(node !is PtProgram && node.definingSub() in subdefers) {
|
||||
when (node) {
|
||||
is PtReturn -> returnsToAugment.add(node)
|
||||
is PtFunctionCall -> {
|
||||
if (node.name.startsWith("sys.exit"))
|
||||
jumpsAndCallsToAugment.add(node)
|
||||
}
|
||||
is PtJump -> {
|
||||
if (node.identifier != null) {
|
||||
val stNode = st.lookup(node.identifier!!.name)!!
|
||||
val targetSub = stNode.astNode.definingSub()
|
||||
if (targetSub != node.definingSub())
|
||||
jumpsAndCallsToAugment.add(node)
|
||||
}
|
||||
}
|
||||
is PtSub -> {
|
||||
val lastStmt = node.children.lastOrNull { it !is PtDefer }
|
||||
if (lastStmt != null && lastStmt !is PtReturn && lastStmt !is PtJump)
|
||||
subEndsToAugment.add(node)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
is PtReturn -> returnsToAugment.add(node)
|
||||
is PtSub -> {
|
||||
val lastStmt = node.children.lastOrNull { it !is PtDefer }
|
||||
if(lastStmt != null && lastStmt !is PtReturn && lastStmt !is PtJump)
|
||||
subEndsToAugment.add(node)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
fun invokedeferbefore(node: PtNode) {
|
||||
val defer = node.definingSub()!!.children.singleOrNull { it is PtDefer }
|
||||
if (defer != null) {
|
||||
val idx = node.parent.children.indexOf(node)
|
||||
val invokedefer = PtBuiltinFunctionCall("invoke_defer", true, false, DataType.UNDEFINED, node.position)
|
||||
node.parent.add(idx, invokedefer)
|
||||
}
|
||||
val idx = node.parent.children.indexOf(node)
|
||||
val invokedefer = PtFunctionCall(node.definingSub()!!.scopedName+"."+invokeDefersRoutineName, true, DataType.UNDEFINED, node.position)
|
||||
node.parent.add(idx, invokedefer)
|
||||
}
|
||||
|
||||
fun notComplex(value: PtExpression): Boolean = when(value) {
|
||||
is PtAddressOf -> value.arrayIndexExpr == null || notComplex(value.arrayIndexExpr!!)
|
||||
is PtBuiltinFunctionCall -> {
|
||||
@ -96,21 +125,13 @@ private fun integrateDefers(program: PtProgram, st: SymbolTable) {
|
||||
else -> false
|
||||
}
|
||||
|
||||
// calls (sys.exit) exits
|
||||
for(call in callsToAugment) {
|
||||
// jumps and calls (sys.exit) exits
|
||||
for(call in jumpsAndCallsToAugment) {
|
||||
invokedeferbefore(call)
|
||||
}
|
||||
|
||||
// jump exits
|
||||
for(exit in jumpsToAugment) {
|
||||
invokedeferbefore(exit)
|
||||
}
|
||||
|
||||
// return exits
|
||||
for(ret in returnsToAugment) {
|
||||
val defer = ret.definingSub()!!.children.singleOrNull { it is PtDefer }
|
||||
if(defer == null)
|
||||
continue
|
||||
val value = ret.value
|
||||
if(value==null || notComplex(value)) {
|
||||
invokedeferbefore(ret)
|
||||
@ -123,7 +144,7 @@ private fun integrateDefers(program: PtProgram, st: SymbolTable) {
|
||||
newRet.add(popCall)
|
||||
val group = PtNodeGroup()
|
||||
group.add(pushCall)
|
||||
group.add(PtBuiltinFunctionCall("invoke_defer", true, false, DataType.UNDEFINED, ret.position))
|
||||
group.add(PtBuiltinFunctionCall(invokeDefersRoutineName, true, false, DataType.UNDEFINED, ret.position))
|
||||
group.add(newRet)
|
||||
group.parent = ret.parent
|
||||
val idx = ret.parent.children.indexOf(ret)
|
||||
@ -137,53 +158,41 @@ private fun integrateDefers(program: PtProgram, st: SymbolTable) {
|
||||
val idx = sub.children.indexOfLast { it !is PtDefer }
|
||||
val ret = PtReturn(sub.position)
|
||||
sub.add(idx+1, ret)
|
||||
val invokedefer = PtBuiltinFunctionCall("invoke_defer", true, false, DataType.UNDEFINED, sub.position)
|
||||
val invokedefer = PtBuiltinFunctionCall(invokeDefersRoutineName, true, false, DataType.UNDEFINED, sub.position)
|
||||
sub.add(idx+1, invokedefer)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* start of new defer implementation:
|
||||
|
||||
private fun integrateDefers(program: PtProgram, errors: IErrorReporter) {
|
||||
val defersPerSub = mutableMapOf<PtSub, MutableList<PtDefer>>().withDefault { mutableListOf() }
|
||||
|
||||
walkAst(program) { node, _ ->
|
||||
if(node is PtDefer) {
|
||||
val scope = node.definingSub()!!
|
||||
val defers = defersPerSub.getValue(scope)
|
||||
defers.add(node)
|
||||
defersPerSub[scope] = defers
|
||||
for( (sub, defers) in subdefers) {
|
||||
// create the routine that calls the enabled defers in reverse order
|
||||
val defersRoutine = PtSub(invokeDefersRoutineName, emptyList(), null, Position.DUMMY)
|
||||
defersRoutine.parent=sub
|
||||
for((idx, defer) in defers.reversed().withIndex()) {
|
||||
val shift = PtAugmentedAssign(">>=", Position.DUMMY)
|
||||
shift.add(PtAssignTarget(false, sub.position).also {
|
||||
it.add(PtIdentifier(sub.scopedName+"."+maskVarName, DataType.UBYTE, sub.position))
|
||||
})
|
||||
shift.add(PtNumber(DataType.UBYTE, 1.0, sub.position))
|
||||
defersRoutine.add(shift)
|
||||
val skiplabel = "prog8_defer_skip_${idx+1}"
|
||||
val branchcc = PtConditionalBranch(BranchCondition.CC, Position.DUMMY)
|
||||
branchcc.add(PtNodeGroup().also {
|
||||
it.add(PtJump(PtIdentifier(defersRoutine.scopedName+"."+skiplabel, DataType.UNDEFINED, Position.DUMMY), null, Position.DUMMY))
|
||||
})
|
||||
branchcc.add(PtNodeGroup())
|
||||
defersRoutine.add(branchcc)
|
||||
for(c in defer.children) {
|
||||
defersRoutine.add(c)
|
||||
}
|
||||
defersRoutine.add(PtLabel(skiplabel, Position.DUMMY))
|
||||
}
|
||||
}
|
||||
// val printMask = PtFunctionCall("txt.print_ubbin", true, DataType.UNDEFINED, Position.DUMMY)
|
||||
// printMask.add(PtIdentifier(sub.scopedName+"."+maskVarName, DataType.UBYTE, Position.DUMMY))
|
||||
// printMask.add(PtBool(true, Position.DUMMY))
|
||||
// defersRoutine.add(printMask)
|
||||
|
||||
val maskVarName = "prog8_defers_mask"
|
||||
|
||||
for((sub, defers) in defersPerSub) {
|
||||
|
||||
if(defers.isEmpty())
|
||||
continue
|
||||
if (defers.size > 8) {
|
||||
errors.err("can have no more than 8 defers per subroutine", sub.position)
|
||||
return
|
||||
}
|
||||
|
||||
val deferVariable = PtVariable(maskVarName, DataType.UBYTE, ZeropageWish.NOT_IN_ZEROPAGE, null, null, sub.position)
|
||||
sub.add(0, deferVariable)
|
||||
|
||||
for((deferIndex, defer) in defers.withIndex()) {
|
||||
val idx = defer.parent.children.indexOf(defer)
|
||||
val enableDefer = PtAugmentedAssign("|=", defer.position)
|
||||
val target = PtAssignTarget(true, defer.position)
|
||||
target.add(PtIdentifier(sub.scopedName+"."+maskVarName, DataType.UBYTE, defer.position))
|
||||
enableDefer.add(target)
|
||||
enableDefer.add(PtNumber(DataType.UBYTE, (1 shl deferIndex).toDouble(), defer.position))
|
||||
sub.add(idx, enableDefer)
|
||||
}
|
||||
defersRoutine.add(PtReturn(Position.DUMMY))
|
||||
sub.add(defersRoutine)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*/
|
@ -6,7 +6,6 @@ why is 0 as value stored as null in symboltablemaker?
|
||||
|
||||
are variables initialized with 0 reset to 0 with an assignment? WHY is the BSS area then cleared with memset? shouldn't be necessary?
|
||||
|
||||
- defers that haven't been reached yet should not be executed (how will we do this? some kind of runtime support needed? refcount or bitmask, not a boolean var per defer that would be wasteful)
|
||||
- unit test for defer
|
||||
- describe defer in the manual
|
||||
|
||||
|
@ -4,25 +4,8 @@
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
|
||||
ubyte @shared c=99
|
||||
if c>100
|
||||
cx16.r0L++
|
||||
cx16.r0L = if (c>100) 2 else (3)
|
||||
txt.print_ub(if (c>100) 2 else 3)
|
||||
txt.nl()
|
||||
txt.print_ub(if (c<100) 6 else 7)
|
||||
txt.nl()
|
||||
|
||||
float @shared fl=99.99
|
||||
floats.print(if (c>100) 2.22 else 3.33)
|
||||
txt.nl()
|
||||
floats.print(if (c<100) 6.66 else 7.77)
|
||||
txt.nl()
|
||||
|
||||
uword res1 = allocate(111)
|
||||
defer deallocate(res1)
|
||||
uword res2 = allocate(222)
|
||||
@ -37,6 +20,11 @@ main {
|
||||
}
|
||||
|
||||
sub allocate(uword arg) -> uword {
|
||||
; if arg==222
|
||||
; return 0
|
||||
txt.print("allocate ")
|
||||
txt.print_uw(4000+arg)
|
||||
txt.nl()
|
||||
return 4000+arg
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user