From 34aa21f7d97b6ac427709cd761cb95cd9949deb4 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 22 Oct 2023 22:25:11 +0200 Subject: [PATCH] improve function call arg type casting --- compiler/res/prog8lib/cx16/syslib.p8 | 4 +- .../compiler/astprocessing/TypecastsAdder.kt | 141 +++++++----------- docs/source/todo.rst | 2 - 3 files changed, 54 insertions(+), 93 deletions(-) diff --git a/compiler/res/prog8lib/cx16/syslib.p8 b/compiler/res/prog8lib/cx16/syslib.p8 index 7c333d898..f5338479a 100644 --- a/compiler/res/prog8lib/cx16/syslib.p8 +++ b/compiler/res/prog8lib/cx16/syslib.p8 @@ -407,7 +407,7 @@ romsub $febd = kbdbuf_peek2() -> uword @AX ; alternative to above to romsub $fec0 = kbdbuf_get_modifiers() -> ubyte @A romsub $fec3 = kbdbuf_put(ubyte key @A) clobbers(X) romsub $fed2 = keymap(uword identifier @XY, bool read @Pc) -> bool @Pc -romsub $ff68 = mouse_config(ubyte shape @A, ubyte resX @X, ubyte resY @Y) clobbers (A, X, Y) +romsub $ff68 = mouse_config(byte shape @A, ubyte resX @X, ubyte resY @Y) clobbers (A, X, Y) romsub $ff6b = mouse_get(ubyte zpdataptr @X) -> ubyte @A romsub $ff71 = mouse_scan() clobbers(A, X, Y) romsub $ff53 = joystick_scan() clobbers(A, X, Y) @@ -450,7 +450,7 @@ asmsub kbdbuf_clear() { }} } -asmsub mouse_config2(ubyte shape @A) clobbers (A, X, Y) { +asmsub mouse_config2(byte shape @A) clobbers (A, X, Y) { ; -- convenience wrapper function that handles the screen resolution for mouse_config() for you %asm {{ pha ; save shape diff --git a/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt b/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt index 706f7798e..8a399b915 100644 --- a/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt +++ b/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt @@ -9,6 +9,7 @@ import prog8.ast.statements.* import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification import prog8.code.core.* +import kotlin.math.sign class TypecastsAdder(val program: Program, val options: CompilationOptions, val errors: IErrorReporter) : AstWalker() { @@ -245,96 +246,54 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val private fun afterFunctionCallArgs(call: IFunctionCall): Iterable { // see if a typecast is needed to convert the arguments into the required parameter's type val modifications = mutableListOf() - - when(val sub = call.target.targetStatement(program)) { - is Subroutine -> { - sub.parameters.zip(call.args).forEach { (param, arg) -> - val argItype = arg.inferType(program) - if(argItype.isKnown) { - val argtype = argItype.getOr(DataType.UNDEFINED) - val requiredType = param.type - if (requiredType != argtype) { - if (argtype isAssignableTo requiredType) { - // don't need a cast for pass-by-reference types that are assigned to UWORD - if(requiredType!=DataType.UWORD || argtype !in PassByReferenceDatatypes) - addTypecastOrCastedValueModification(modifications, arg, requiredType, call as Node) - } else if(requiredType == DataType.UWORD && argtype in PassByReferenceDatatypes) { - // We allow STR/ARRAY values in place of UWORD parameters. - // Take their address instead, UNLESS it's a str parameter in the containing subroutine - val identifier = arg as? IdentifierReference - if(identifier?.isSubroutineParameter(program)==false) { - modifications += IAstModification.ReplaceNode( - identifier, - AddressOf(identifier, null, arg.position), - call as Node) - } - } else if(arg is NumericLiteral) { - val cast = arg.cast(requiredType) - if(cast.isValid) - modifications += IAstModification.ReplaceNode( - arg, - cast.valueOrZero(), - call as Node) - } else if(requiredType==DataType.BOOL && argtype!=DataType.BOOL) { - // cast to bool - addTypecastOrCastedValueModification(modifications, arg, requiredType, call as Node) - } - } - } - else { - // if the argument is an identifier reference and the param is UWORD, add the missing &. - if(arg is IdentifierReference && DataType.UWORD == param.type) { - modifications += IAstModification.ReplaceNode( - arg, - AddressOf(arg, null, arg.position), - call as Node - ) - } - } - } - } - is BuiltinFunctionPlaceholder -> { - val func = BuiltinFunctions.getValue(sub.name) - func.parameters.zip(call.args).forEachIndexed { index, pair -> - val argItype = pair.second.inferType(program) - if (argItype.isKnown) { - val argtype = argItype.getOr(DataType.UNDEFINED) - if (pair.first.possibleDatatypes.all { argtype != it }) { - for (possibleType in pair.first.possibleDatatypes) { - if (argtype isAssignableTo possibleType) { - addTypecastOrCastedValueModification(modifications, pair.second, possibleType, call as Node) - break - } - else if(DataType.UWORD in pair.first.possibleDatatypes && argtype in PassByReferenceDatatypes) { - // We allow STR/ARRAY values in place of UWORD parameters. - // Take their address instead, UNLESS it's a str parameter in the containing subroutine - val identifier = pair.second as? IdentifierReference - if(identifier?.isSubroutineParameter(program)==false) { - modifications += IAstModification.ReplaceNode( - call.args[index], - AddressOf(identifier, null, pair.second.position), - call as Node) - break - } - } - } - } - } - else { - // if the argument is an identifier reference and the param is UWORD, add the missing &. - if(pair.second is IdentifierReference && DataType.UWORD in pair.first.possibleDatatypes) { - modifications += IAstModification.ReplaceNode( - call.args[index], - AddressOf(pair.second as IdentifierReference, null, pair.second.position), - call as Node - ) - } - } - } - } - else -> { } + val sub = call.target.targetStatement(program) + val params = when(sub) { + is BuiltinFunctionPlaceholder -> BuiltinFunctions.getValue(sub.name).parameters + is Subroutine -> sub.parameters.map { FParam(it.name, listOf(it.type).toTypedArray()) } + else -> emptyList() } + params.zip(call.args).forEach { + val targetDt = it.first.possibleDatatypes.first() + val argIdt = it.second.inferType(program) + if (argIdt.isKnown) { + val argDt = argIdt.getOr(DataType.UNDEFINED) + if (argDt !in it.first.possibleDatatypes) { + val identifier = it.second as? IdentifierReference + val number = it.second as? NumericLiteral + if(number!=null) { + addTypecastOrCastedValueModification(modifications, it.second, targetDt, call as Node) + } else if(identifier!=null && targetDt==DataType.UWORD && argDt in PassByReferenceDatatypes) { + if(!identifier.isSubroutineParameter(program)) { + // We allow STR/ARRAY values for UWORD parameters. + // If it's an array (not STR), take the address. + if(argDt != DataType.STR) { + modifications += IAstModification.ReplaceNode( + identifier, + AddressOf(identifier, null, it.second.position), + call as Node + ) + } + } + } else if(targetDt==DataType.BOOL) { + addTypecastOrCastedValueModification(modifications, it.second, DataType.BOOL, call as Node) + } else if(argDt isAssignableTo targetDt) { + if(argDt!=DataType.STR || targetDt!=DataType.UWORD) + addTypecastOrCastedValueModification(modifications, it.second, targetDt, call as Node) + } + } + } else { + val identifier = it.second as? IdentifierReference + if(identifier!=null && targetDt==DataType.UWORD) { + // take the address of the identifier + modifications += IAstModification.ReplaceNode( + identifier, + AddressOf(identifier, null, it.second.position), + call as Node + ) + } + } + } return modifications } @@ -448,7 +407,11 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val if(expressionToCast is NumericLiteral && expressionToCast.type!=DataType.FLOAT) { // refuse to automatically truncate floats val castedValue = expressionToCast.cast(requiredType) if (castedValue.isValid) { - modifications += IAstModification.ReplaceNode(expressionToCast, castedValue.valueOrZero(), parent) + val signOriginal = sign(expressionToCast.number) + val signCasted = sign(castedValue.valueOrZero().number) + if(signOriginal==signCasted) { + modifications += IAstModification.ReplaceNode(expressionToCast, castedValue.valueOrZero(), parent) + } return } } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 90f7d49d1..588c7064a 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,8 +1,6 @@ TODO ==== -- don't add implicit type casts to function call arguments, so diskio.f_read(bytevar, 0) will become a compilation error - - [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: ir-less-branch-opcodes] IR: reduce the number of branch instructions such as BEQ, BEQR, etc (gradually), replace with CMP(I) + status branch instruction - 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!