mirror of
https://github.com/irmen/prog8.git
synced 2025-01-03 06:29:54 +00:00
adding short-circuit boolean expression evaluation (in IR codegen) also -noshortcircuit cli option
This commit is contained in:
parent
813007a5d8
commit
f790182f0b
@ -18,6 +18,7 @@ class CompilationOptions(val output: OutputType,
|
||||
var optimize: Boolean = false,
|
||||
var asmQuiet: Boolean = false,
|
||||
var asmListfile: Boolean = false,
|
||||
var shortCircuit: Boolean = true,
|
||||
var includeSourcelines: Boolean = false,
|
||||
var experimentalCodegen: Boolean = false,
|
||||
var varsHighBank: Int? = null,
|
||||
|
@ -697,6 +697,17 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
|
||||
private fun operatorAnd(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(codeGen.options.shortCircuit && (!binExpr.left.isSimple() && !binExpr.right.isSimple())) {
|
||||
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val shortcutLabel = codeGen.createLabelName()
|
||||
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = shortcutLabel), null)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, leftTr.resultReg, -1)
|
||||
result += IRCodeChunk(shortcutLabel, null)
|
||||
return ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
||||
} else {
|
||||
return if(binExpr.right is PtNumber) {
|
||||
val tr = translateExpression(binExpr.left)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
@ -711,9 +722,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun operatorOr(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(codeGen.options.shortCircuit && (!binExpr.left.isSimple() && !binExpr.right.isSimple())) {
|
||||
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val shortcutLabel = codeGen.createLabelName()
|
||||
addInstr(result, IRInstruction(Opcode.BSTNE, labelSymbol = shortcutLabel), null)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, leftTr.resultReg, -1)
|
||||
result += IRCodeChunk(shortcutLabel, null)
|
||||
return ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
||||
} else {
|
||||
return if(binExpr.right is PtNumber) {
|
||||
val tr = translateExpression(binExpr.left)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
@ -728,6 +751,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun operatorModulo(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult {
|
||||
require(vmDt!=IRDataType.FLOAT) {"floating-point modulo not supported ${binExpr.position}"}
|
||||
|
@ -50,6 +50,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||
val includeSourcelines by cli.option(ArgType.Boolean, fullName = "sourcelines", description = "include original Prog8 source lines in generated asm code")
|
||||
val splitWordArrays by cli.option(ArgType.Boolean, fullName = "splitarrays", description = "treat all word arrays as tagged with @split to make them lsb/msb split in memory")
|
||||
val noShortCircuit by cli.option(ArgType.Boolean, fullName = "noshortcircuit", description = "do not apply McCarthy/short-circuit evaluation to boolean expressions")
|
||||
val breakpointCpuInstruction by cli.option(ArgType.Boolean, fullName = "breakinstr", description = "also use a CPU instruction for %breakpoint")
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${PETTarget.NAME}', '${VMTarget.NAME}') (required)")
|
||||
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
|
||||
@ -148,6 +149,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
warnSymbolShadowing == true,
|
||||
quietAssembler == true,
|
||||
asmListfile == true,
|
||||
noShortCircuit != true,
|
||||
includeSourcelines == true,
|
||||
experimentalCodegen == true,
|
||||
varsHighBank,
|
||||
@ -224,6 +226,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
warnSymbolShadowing == true,
|
||||
quietAssembler == true,
|
||||
asmListfile == true,
|
||||
noShortCircuit != true,
|
||||
includeSourcelines == true,
|
||||
experimentalCodegen == true,
|
||||
varsHighBank,
|
||||
|
@ -33,6 +33,7 @@ class CompilerArguments(val filepath: Path,
|
||||
val warnSymbolShadowing: Boolean,
|
||||
val quietAssembler: Boolean,
|
||||
val asmListfile: Boolean,
|
||||
val shortCircuit: Boolean,
|
||||
val includeSourcelines: Boolean,
|
||||
val experimentalCodegen: Boolean,
|
||||
val varsHighBank: Int?,
|
||||
@ -76,6 +77,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
optimize = args.optimize
|
||||
asmQuiet = args.quietAssembler
|
||||
asmListfile = args.asmListfile
|
||||
shortCircuit = args.shortCircuit
|
||||
includeSourcelines = args.includeSourcelines
|
||||
experimentalCodegen = args.experimentalCodegen
|
||||
breakpointCpuInstruction = args.breakpointCpuInstruction
|
||||
|
@ -30,6 +30,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
|
||||
warnSymbolShadowing = false,
|
||||
quietAssembler = true,
|
||||
asmListfile = false,
|
||||
shortCircuit = true,
|
||||
includeSourcelines = false,
|
||||
experimentalCodegen = false,
|
||||
varsHighBank = null,
|
||||
|
@ -28,6 +28,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
|
||||
warnSymbolShadowing = false,
|
||||
quietAssembler = true,
|
||||
asmListfile = false,
|
||||
shortCircuit = true,
|
||||
includeSourcelines = false,
|
||||
experimentalCodegen = false,
|
||||
varsHighBank = null,
|
||||
|
@ -27,6 +27,7 @@ internal fun compileFile(
|
||||
warnSymbolShadowing = false,
|
||||
quietAssembler = true,
|
||||
asmListfile = false,
|
||||
shortCircuit = true,
|
||||
includeSourcelines = false,
|
||||
experimentalCodegen = false,
|
||||
varsHighBank = null,
|
||||
|
@ -195,6 +195,11 @@ One or more .p8 module files
|
||||
This removes the need to add @split yourself but some programs may fail to compile with
|
||||
this option as not all array operations are implemented yet on split arrays.
|
||||
|
||||
``-noshortcircuit``
|
||||
Do *not* apply `McCarthy/short-circuit evaluation <https://en.wikipedia.org/wiki/Short-circuit_evaluation>`_ to boolean expressions.
|
||||
This is a new feature and changes the behavior of existing programs so it can be turned off again for now.
|
||||
This toggle will disappear eventually.
|
||||
|
||||
``-vm``
|
||||
load and run a p8-virt or p8-ir listing in the internal VirtualMachine instead of compiling a prog8 program file..
|
||||
|
||||
|
@ -2,7 +2,11 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
- [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 ....
|
||||
- [on branch: shortcircuit] complete McCarthy evaluation. This may also reduce code size perhaps for things like if a>4 or a<2 ....
|
||||
- vm ircodegen (DONE)
|
||||
- in 6502 codegen (see vm's ExpressionGen operatorAnd / operatorOr)
|
||||
|
||||
- IR: reduce amount of CMP/CMPI after instructions that set the status bits correctly (LOADs? INC? Bitwise operations, etc), but only after setting the status bits is verified!
|
||||
|
||||
...
|
||||
|
||||
@ -11,7 +15,6 @@ Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Compiler:
|
||||
|
||||
- (after shortcircuit is in:) What happens when we make all subs return a boolean not as ubyte in A, but in the cpu's Carry flag?
|
||||
- Multidimensional arrays and chained indexing, purely as syntactic sugar over regular arrays.
|
||||
- make a form of "manual generics" possible like: varsub routine(T arg)->T where T is expanded to a specific type
|
||||
(this is already done hardcoded for several of the builtin functions)
|
||||
@ -27,7 +30,6 @@ Compiler:
|
||||
- (need separate step in codegen and IR to write the "golden" variables)
|
||||
|
||||
- do we need (array)variable alignment tag instead of block alignment tag? You want to align the data, not the code in the block?
|
||||
- ir: proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg
|
||||
- ir: getting it in shape for code generation
|
||||
- ir: related to the one above: block alignment doesn't translate well to variables in the block (the actual stuff that needs to be aligned in memory) but: need variable alignment tag instead of block alignment tag, really
|
||||
- ir: idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)
|
||||
@ -48,7 +50,7 @@ Compiler:
|
||||
|
||||
Libraries:
|
||||
|
||||
- once a VAL_1 implementation is merged into the X16 kernal properly, remove all the workarounds in cx16 floats.parse_f() . Prototype parse routine in examples/cx16/floatparse.p8
|
||||
- once a VAL_1 implementation is merged into the X16 kernal properly, remove all the workarounds in cx16 floats.parse_f()
|
||||
- fix the problems in atari target, and flesh out its libraries.
|
||||
- c128 target: make syslib more complete (missing kernal routines)?
|
||||
- pet32 target: make syslib more complete (missing kernal routines)?
|
||||
@ -56,6 +58,8 @@ Libraries:
|
||||
|
||||
Optimizations:
|
||||
|
||||
- give a warning for variables that could be a const - or even make them a const (if not @shared)?
|
||||
- treat every scalar variable decl with initialization value, as const by default, unless the variable gets assigned to somewhere (or has its address taken, or is @shared)
|
||||
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
|
||||
for instance, vars used inside loops first, then loopvars, then uwords used as pointers, then the rest
|
||||
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once 6502-codegen is done from IR code,
|
||||
@ -78,6 +82,9 @@ What if we were to re-introduce Structs in prog8? Some thoughts:
|
||||
Other language/syntax features to think about
|
||||
---------------------------------------------
|
||||
|
||||
- add (rom/ram)bank support to romsub. A call will then automatically switch banks, use callfar and something else when in banked ram.
|
||||
challenges: how to not make this too X16 specific? How does the compiler know what bank to switch (ram/rom)?
|
||||
How to make it performant when we want to (i.e. NOT have it use callfar/auto bank switching) ?
|
||||
- module directive to set the text encoding for that whole file (iso, petscii, etc.)
|
||||
- chained assignments `x=y=z=99`
|
||||
- declare multiple variables `ubyte x,y,z` (if init value present, all get that init value)
|
||||
- chained comparisons `10<x<20` , `x==y==z` (desugars to `10<x and x<20`, `x==y and y==z`)
|
||||
- postincrdecr as expression, preincrdecr expression (`y = x++`, `y = ++x`) .... is this even possible, expression with side effects like this?
|
||||
- negative array index to refer to an element from the end of the array. Python `[-1]` or Raku syntax `[\*-1]` , `[\*/2]` .... \*=size of the array
|
||||
|
@ -1,21 +1,67 @@
|
||||
%import math
|
||||
%import string
|
||||
%import emudbg
|
||||
%import palette
|
||||
%import floats
|
||||
%import textio
|
||||
%zeropage dontuse
|
||||
|
||||
main {
|
||||
sub start () {
|
||||
emudbg.console_value1(123)
|
||||
emudbg.console_value2(99)
|
||||
emudbg.console_chrout('@')
|
||||
emudbg.console_chrout('h')
|
||||
emudbg.console_chrout('e')
|
||||
emudbg.console_chrout('l')
|
||||
emudbg.console_chrout('l')
|
||||
emudbg.console_chrout('o')
|
||||
emudbg.console_chrout('\n')
|
||||
ubyte a1 = 10
|
||||
ubyte a2 = 20
|
||||
ubyte x1 = 30
|
||||
ubyte x2 = 40
|
||||
ubyte zero = 0
|
||||
|
||||
txt.print("1a:\n")
|
||||
if calc_a1()<calc_x1() and calc_a2()<=calc_x2()
|
||||
txt.print("* 1a and ok\n")
|
||||
|
||||
txt.print("\n1b:\n")
|
||||
if calc_a1()<calc_x1() and calc_a2()>calc_x2()
|
||||
txt.print("* 1b and fail\n")
|
||||
|
||||
txt.print("\n1c:\n")
|
||||
if calc_a1()>calc_x1() and calc_a2()<=calc_x2()
|
||||
txt.print("* 1c and fail\n")
|
||||
|
||||
txt.print("\n2a:\n")
|
||||
if calc_a1()<calc_x1() or calc_a2()<=calc_x2()
|
||||
txt.print("* 2a or ok\n")
|
||||
|
||||
txt.print("\n2b:\n")
|
||||
if calc_a1()<calc_x1() or calc_a2()>calc_x2()
|
||||
txt.print("* 2b or ok\n")
|
||||
|
||||
txt.print("\n3a:\n")
|
||||
if calc_a1()>calc_x1() or calc_a2()<=calc_x2()
|
||||
txt.print("* 3a or ok\n")
|
||||
|
||||
txt.print("\n3b:\n")
|
||||
if calc_a1()>calc_x1() or calc_a2()>calc_x2()
|
||||
txt.print("* 3b or fail\n")
|
||||
|
||||
txt.print("\n4a:\n")
|
||||
bool result = calc_a1()<calc_x1() or calc_a2()>calc_x2()
|
||||
txt.print_ub(result)
|
||||
txt.nl()
|
||||
txt.print("\n4b:\n")
|
||||
result = calc_a1()>=calc_x1() and calc_a2()>calc_x2()
|
||||
txt.print_ub(result)
|
||||
txt.nl()
|
||||
@($4000) &= 22
|
||||
|
||||
sub calc_a1() -> ubyte {
|
||||
txt.print("calc_a1\n")
|
||||
return a1+zero
|
||||
}
|
||||
sub calc_a2() -> ubyte {
|
||||
txt.print("calc_a2\n")
|
||||
return a2+zero
|
||||
}
|
||||
sub calc_x1() -> ubyte {
|
||||
txt.print("calc_x1\n")
|
||||
return x1+zero
|
||||
}
|
||||
sub calc_x2() -> ubyte {
|
||||
txt.print("calc_x2\n")
|
||||
return x2+zero
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ class RequestParser : Take {
|
||||
quietAssembler = false,
|
||||
includeSourcelines = false,
|
||||
asmListfile = false,
|
||||
shortCircuit = true,
|
||||
experimentalCodegen = false,
|
||||
splitWordArrays = false,
|
||||
breakpointCpuInstruction = false,
|
||||
|
@ -14,10 +14,10 @@ Program to execute is not stored in the system memory, it's just a separate list
|
||||
65536 virtual floating point registers (64 bits double precision) fr0-fr65535
|
||||
65536 bytes of memory. Thus memory pointers (addresses) are limited to 16 bits.
|
||||
Value stack, max 128 entries of 1 byte each.
|
||||
Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by the CMP instruction or explicit CLC/SEC!!!
|
||||
LOAD instructions DO affect the Z and N flags
|
||||
INC/DEC instructions DO affect the Z and N flags
|
||||
other instructions such as logical or arithmetic operations DO NOT AFFECT THE STATUS FLAGS UNLESS EXPLICITLY NOTED!
|
||||
Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by the CMP instruction or explicit CLC/SEC,
|
||||
LOAD instructions DO affect the Z and N flags.
|
||||
INC/DEC instructions DO affect the Z and N flags,
|
||||
other instructions only affect Z an N flags if the value in a result register is written.
|
||||
|
||||
Instruction set is mostly a load/store architecture, there are few instructions operating on memory directly.
|
||||
|
||||
|
@ -311,8 +311,16 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
|
||||
private inline fun setResultReg(reg: Int, value: Int, type: IRDataType) {
|
||||
when(type) {
|
||||
IRDataType.BYTE -> registers.setUB(reg, value.toUByte())
|
||||
IRDataType.WORD -> registers.setUW(reg, value.toUShort())
|
||||
IRDataType.BYTE -> {
|
||||
registers.setUB(reg, value.toUByte())
|
||||
statusZero = value==0
|
||||
statusNegative = value>=0x80
|
||||
}
|
||||
IRDataType.WORD -> {
|
||||
registers.setUW(reg, value.toUShort())
|
||||
statusZero = value==0
|
||||
statusNegative = value>=0x8000
|
||||
}
|
||||
IRDataType.FLOAT -> throw IllegalArgumentException("attempt to set integer result register but float type")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user