added %option ignore_unused to suppress warnings about unused vars and subs in that module/block.

Also improved error for invalid directive.
This commit is contained in:
Irmen de Jong 2023-12-26 23:37:59 +01:00
parent 2eb137618e
commit 2b8f613a00
52 changed files with 96 additions and 57 deletions

View File

@ -86,6 +86,7 @@ class PtBlock(name: String,
val forceOutput: Boolean = false,
val noSymbolPrefixing: Boolean = false,
val veraFxMuls: Boolean = false,
val ignoreUnused: Boolean = false,
val alignment: BlockAlignment = BlockAlignment.NONE)
}

View File

@ -1667,6 +1667,7 @@ class IRCodeGen(
block.options.forceOutput,
block.options.noSymbolPrefixing,
block.options.veraFxMuls,
block.options.ignoreUnused,
translate(block.options.alignment)
), block.position)
for (child in block.children) {

View File

@ -1,7 +1,6 @@
package prog8.codegen.intermediate
import prog8.code.core.IErrorReporter
import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX
import prog8.intermediate.*
@ -61,7 +60,7 @@ class IRUnusedCodeRemover(
irprog.blocks.forEach { block ->
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
if(sub.isEmpty()) {
if(!sub.position.file.startsWith(LIBRARYFILEPREFIX)) {
if(!block.options.ignoreUnused) {
errors.warn("unused subroutine '${sub.label}'", sub.position)
}
block.children.remove(sub)
@ -82,7 +81,7 @@ class IRUnusedCodeRemover(
irprog.blocks.forEach { block ->
block.children.filterIsInstance<IRAsmSubroutine>().reversed().forEach { sub ->
if(sub.isEmpty()) {
if(!sub.position.file.startsWith(LIBRARYFILEPREFIX)) {
if(!block.options.ignoreUnused) {
errors.warn("unused subroutine '${sub.label}'", sub.position)
}
block.children.remove(sub)

View File

@ -90,7 +90,7 @@ class UnusedCodeRemover(private val program: Program,
if (subroutine !== program.entrypoint && !forceOutput && !subroutine.isAsmSubroutine) {
if(callgraph.unused(subroutine)) {
if(subroutine.containsNoCodeNorVars) {
if(!subroutine.definingModule.isLibrary)
if("ignore_unused" !in subroutine.definingBlock.options())
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
val removals = mutableListOf(IAstModification.Remove(subroutine, parent as IStatementContainer))
callgraph.calledBy[subroutine]?.let {
@ -99,7 +99,7 @@ class UnusedCodeRemover(private val program: Program,
}
return removals
}
if(!subroutine.hasBeenInlined && !subroutine.definingModule.isLibrary) {
if(!subroutine.hasBeenInlined && "ignore_unused" !in subroutine.definingBlock.options()) {
errors.warn("unused subroutine '${subroutine.name}'", subroutine.position)
}
if(!subroutine.inline) {
@ -119,7 +119,7 @@ class UnusedCodeRemover(private val program: Program,
if (!forceOutput && decl.origin==VarDeclOrigin.USERCODE && !decl.sharedWithAsm) {
val usages = callgraph.usages(decl)
if (usages.isEmpty()) {
if(!decl.definingModule.isLibrary)
if("ignore_unused" !in decl.definingBlock.options())
errors.warn("removing unused variable '${decl.name}'", decl.position)
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
}
@ -131,7 +131,7 @@ class UnusedCodeRemover(private val program: Program,
if(assignment!=null && assignment.origin==AssignmentOrigin.VARINIT) {
if(assignment.value.isSimple) {
// remove the vardecl
if(!decl.definingModule.isLibrary)
if("ignore_unused" !in decl.definingBlock.options())
errors.warn("removing unused variable '${decl.name}'", decl.position)
return listOf(
IAstModification.Remove(decl, parent as IStatementContainer),

View File

@ -1,7 +1,7 @@
; Prog8 definitions for the Atari800XL
; Including memory registers, I/O registers, Basic and Kernal subroutines.
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
atari {

View File

@ -7,7 +7,7 @@
txt {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 24

View File

@ -1,7 +1,7 @@
; Prog8 definitions for the Commodore-128
; Including memory registers, I/O registers, Basic and Kernal subroutines.
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
cbm {
; Commodore (CBM) common variables, vectors and kernal routines

View File

@ -7,7 +7,7 @@
txt {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 25

View File

@ -1,6 +1,6 @@
; Prog8 definitions for floating point handling on the Commodore-64
%option enable_floats, no_symbol_prefixing
%option enable_floats, no_symbol_prefixing, ignore_unused
%import floats_functions
floats {

View File

@ -8,7 +8,7 @@
; so that the program itself can be larger without starting to overwrite the graphics memory.
graphics {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
const uword WIDTH = 320
const ubyte HEIGHT = 200

View File

@ -1,7 +1,7 @@
; Prog8 definitions for the Commodore-64
; Including memory registers, I/O registers, Basic and Kernal subroutines.
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
cbm {
; Commodore (CBM) common variables, vectors and kernal routines

View File

@ -8,7 +8,7 @@
txt {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 25

View File

@ -2,7 +2,7 @@
conv {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
; ----- number conversions to decimal strings ----

View File

@ -17,7 +17,7 @@
%import syslib
diskio {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
const ubyte READ_IO_CHANNEL=12
const ubyte WRITE_IO_CHANNEL=13

View File

@ -1,6 +1,6 @@
; Prog8 definitions for floating point handling on the CommanderX16
%option enable_floats, no_symbol_prefixing
%option enable_floats, no_symbol_prefixing, ignore_unused
%import floats_functions
floats {

View File

@ -24,7 +24,7 @@
gfx2 {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
; read-only control variables:
ubyte active_mode = 0

View File

@ -14,7 +14,7 @@
graphics {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
romsub $feff = FB_cursor_position2() clobbers(A,X,Y) ; alias for the normal FB_cursor_position() call but reuses existing r0 and r1

View File

@ -11,7 +11,7 @@
monogfx {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
; read-only control variables:
uword width = 0

View File

@ -3,7 +3,7 @@
; The first 16 colors can be restored to their default with set_default16()
palette {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
uword vera_palette_ptr

View File

@ -1,7 +1,7 @@
%import syslib
psg {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
; $1F9C0 - $1F9FF 16 blocks of 4 PSG registers (16 voices)
; 00 frequency word LSB

View File

@ -1,7 +1,7 @@
; Prog8 definitions for the CommanderX16
; Including memory registers, I/O registers, Basic and Kernal subroutines.
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
cbm {
; Commodore (CBM) common variables, vectors and kernal routines

View File

@ -8,7 +8,7 @@
txt {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
const ubyte DEFAULT_WIDTH = 80
const ubyte DEFAULT_HEIGHT = 60

View File

@ -4,7 +4,7 @@
; https://docs.google.com/document/d/1q34uWOiM3Be2pnaHRVgSdHySI-qsiQWPTo_gfE54PTg/edit
verafx {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
sub available() -> bool {
; returns true if Vera FX is available (Vera V0.3.1 or later), false if not.

View File

@ -3,7 +3,7 @@
%import textio
cx16logo {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
sub logo_at(ubyte column, ubyte row) {
uword strptr

View File

@ -6,7 +6,7 @@
%import syslib
diskio {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
const ubyte READ_IO_CHANNEL=12
const ubyte WRITE_IO_CHANNEL=13

View File

@ -1,6 +1,6 @@
floats {
; the floating point functions shared across compiler targets
%option merge, no_symbol_prefixing
%option merge, no_symbol_prefixing, ignore_unused
asmsub print_f(float value @FAC1) clobbers(A,X,Y) {
; ---- prints the floating point value (without a newline). No leading space (unlike BASIC)!

View File

@ -3,7 +3,7 @@
; such as abs, sqrt, clamp, min, max for example.
math {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
%asminclude "library:math.asm"

View File

@ -2,7 +2,7 @@
; Including memory registers, I/O registers, Basic and Kernal subroutines.
; see: https://www.pagetable.com/?p=926 , http://www.zimmers.net/cbmpics/cbm/PETx/petmem.txt
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
cbm {
; Commodore (CBM) common variables, vectors and kernal routines

View File

@ -7,7 +7,7 @@
txt {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 25

View File

@ -1,7 +1,7 @@
; Internal library routines - always included by the compiler
prog8_lib {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
%asminclude "library:prog8_lib.asm"
%asminclude "library:prog8_funcs.asm"

View File

@ -1,7 +1,7 @@
; 0-terminated string manipulation routines.
string {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
asmsub length(uword string @AY) clobbers(A) -> ubyte @Y {
; Returns the number of bytes in the string.

View File

@ -3,7 +3,7 @@
%import textio
test_stack {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
asmsub test() {
%asm {{

View File

@ -4,6 +4,8 @@ conv {
; ----- number conversions to decimal strings ----
%option ignore_unused
str string_out = "????????????????" ; result buffer for the string conversion routines
sub str_ub0(ubyte value) {

View File

@ -4,6 +4,7 @@
%import textio
emudbg {
%option ignore_unused
sub is_emulator() -> bool {
; Test for emulator presence.

View File

@ -1,6 +1,6 @@
; Prog8 definitions for floating point handling on the VirtualMachine
%option enable_floats
%option enable_floats, ignore_unused
floats {

View File

@ -3,6 +3,7 @@
; such as abs, sqrt, clamp, min, max for example.
math {
%option ignore_unused
sub sin8u(ubyte angle) -> ubyte {
ubyte[256] sintab = [$80, $83, $86, $89, $8c, $8f, $92, $95, $98, $9b, $9e, $a2, $a5, $a7, $aa, $ad, $b0, $b3, $b6, $b9,

View File

@ -7,7 +7,7 @@
monogfx {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
; read-only control variables:
uword width = 0

View File

@ -1,6 +1,6 @@
; Internal library routines - always included by the compiler
prog8_lib {
%option force_output
%option force_output, ignore_unused
}

View File

@ -1,6 +1,8 @@
; 0-terminated string manipulation routines. For the Virtual Machine target.
string {
%option ignore_unused
sub length(str st) -> ubyte {
; Returns the number of bytes in the string.
; This value is determined during runtime and counts upto the first terminating 0 byte in the string,

View File

@ -1,5 +1,7 @@
; Prog8 definitions for the Virtual Machine
%option ignore_unused
sys {
; ------- lowlevel system routines --------

View File

@ -1,6 +1,7 @@
; Prog8 definitions for the Text I/O console routines for the Virtual Machine
%import conv
%option ignore_unused
txt {

View File

@ -843,16 +843,16 @@ internal class AstChecker(private val program: Program,
err("this directive may only occur in a block or at module level")
if(directive.args.isEmpty())
err("missing option directive argument(s)")
else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page", "merge", "splitarrays", "no_symbol_prefixing", "verafxmuls")}.any { !it })
else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page", "merge", "verafxmuls", "splitarrays", "no_symbol_prefixing", "ignore_unused")}.any { !it })
err("invalid option directive argument(s)")
if(directive.args.any {it.name=="align_word"} && directive.args.any { it.name=="align_page"})
err("conflicting alignment options")
if(directive.parent is Block) {
if(directive.args.any {it.name !in arrayOf("align_word", "align_page", "no_symbol_prefixing", "force_output", "merge", "splitarrays", "verafxmuls")})
if(directive.args.any {it.name !in arrayOf("align_word", "align_page", "force_output", "merge", "verafxmuls", "splitarrays", "no_symbol_prefixing", "ignore_unused")})
err("using an option that is not valid for blocks")
}
if(directive.parent is Module) {
if(directive.args.any {it.name !in arrayOf("enable_floats", "no_sysinit", "splitarrays", "no_symbol_prefixing")})
if(directive.args.any {it.name !in arrayOf("enable_floats", "no_sysinit", "splitarrays", "no_symbol_prefixing", "ignore_unused")})
err("using an option that is not valid for modules")
}
if(directive.args.any { it.name=="verafxmuls" } && compilerOptions.compTarget.name != Cx16Target.NAME)

View File

@ -164,7 +164,8 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
var alignment = PtBlock.BlockAlignment.NONE
var forceOutput = false
var veraFxMuls = false
var noSymbolPrefixing = "no_symbol_prefixing" in srcBlock.definingModule.options()
var noSymbolPrefixing = false
var ignoreUnused = false
val directives = srcBlock.statements.filterIsInstance<Directive>()
for (directive in directives.filter { it.directive == "%option" }) {
for (arg in directive.args) {
@ -172,6 +173,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
"align_word" -> alignment = PtBlock.BlockAlignment.WORD
"align_page" -> alignment = PtBlock.BlockAlignment.PAGE
"no_symbol_prefixing" -> noSymbolPrefixing = true
"ignore_unused" -> ignoreUnused = true
"force_output" -> forceOutput = true
"merge", "splitarrays" -> { /* ignore this one */ }
"verafxmuls" -> veraFxMuls = true
@ -182,7 +184,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
val (vardecls, statements) = srcBlock.statements.partition { it is VarDecl }
val src = srcBlock.definingModule.source
val block = PtBlock(srcBlock.name, srcBlock.isInLibrary, src,
PtBlock.Options(srcBlock.address, forceOutput, noSymbolPrefixing, veraFxMuls, alignment),
PtBlock.Options(srcBlock.address, forceOutput, noSymbolPrefixing, veraFxMuls, ignoreUnused, alignment),
srcBlock.position)
makeScopeVarsDecls(vardecls).forEach { block.add(it) }
for (stmt in statements)

View File

@ -14,6 +14,16 @@ import prog8.code.core.*
internal class VariousCleanups(val program: Program, val errors: IErrorReporter, val options: CompilationOptions): AstWalker() {
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
val inheritOptions = block.definingModule.options() intersect setOf("splitarrays", "no_symbol_prefixing", "ignore_unused") subtract block.options()
if(inheritOptions.isNotEmpty()) {
val directive = Directive("%option", inheritOptions.map{ DirectiveArg(null, it, null, block.position) }, block.position)
return listOf(IAstModification.InsertFirst(directive, block))
}
return noModifications
}
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
return if(parent is IStatementContainer)
listOf(ScopeFlatten(scope, parent as IStatementContainer))

View File

@ -361,7 +361,7 @@ private fun ArrayindexContext.toAst() : ArrayIndex =
ArrayIndex(expression().toAst(), toPosition())
internal fun DirectiveContext.toAst() : Directive =
Directive(directivename.text, directivearg().map { it.toAst() }, toPosition())
Directive(DIRECTIVE().text, directivearg().map { it.toAst() }, toPosition())
private fun DirectiveargContext.toAst() : DirectiveArg {
val str = stringliteral()

View File

@ -99,6 +99,23 @@ class Block(override val name: String,
data class Directive(val directive: String, val args: List<DirectiveArg>, override val position: Position) : Statement() {
override lateinit var parent: Node
// init {
// require(directive in arrayOf(
// "%address",
// "%asmbinary",
// "%asminclude",
// "%breakpoint",
// "%encoding",
// "%import",
// "%launcher",
// "%option",
// "%output",
// "%zeropage",
// "%zpallowed",
// "%zpreserved",
// )) { "invalid directive" }
// }
override fun linkParents(parent: Node) {
this.parent = parent
args.forEach{it.linkParents(this)}

View File

@ -147,7 +147,9 @@ Directives
- ``splitarrays`` (block or module) makes all word-arrays in this scope lsb/msb split arrays (as if they all have the @split tag). See Arrays.
- ``no_symbol_prefixing`` (block or module) makes the compiler *not* use symbol-prefixing when translating prog8 code into assembly.
Only use this if you know what you're doing because it could result in invalid assembly code being generated.
It can be useful when writing library modules that you don't want to be exposing prefixed assembly symbols.
This option can be useful when writing library modules that you don't want to be exposing prefixed assembly symbols.
- ``ignore_unused`` (block or module) suppress warnings about unused variables and subroutines. Instead, these will be silently stripped.
This option is useful in library modules that contain many more routines beside the ones that you actually use.
- ``verafxmuls`` (block, cx16 target only) uses Vera FX hardware word multiplication on the CommanderX16 for all word multiplications in this block. Warning: this may interfere with IRQs and other Vera operations, so use this only when you know what you're doing. It's safer to explicitly use ``verafx.muls()``.
.. data:: %encoding <encodingname>

View File

@ -2,9 +2,6 @@
TODO
====
- add -nowarnunused, or %option ignore_unused (module and block scope), to suppress all warnings about unused symbols. Useful in libraries.
put this in all library code, like %option no_symbol_prefixing, and get rid of the "trick" it uses currently to suppress unused symbol warnings for library modules.
- [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 ....
...

View File

@ -350,6 +350,7 @@ class IRFileReader {
attrs.getOrDefault("FORCEOUTPUT", "false").toBoolean(),
attrs.getOrDefault("NOPREFIXING", "false").toBoolean(),
attrs.getOrDefault("VERAFXMULS", "false").toBoolean(),
attrs.getOrDefault("IGNOREUNUSED", "false").toBoolean(),
IRBlock.BlockAlignment.valueOf(attrs.getValue("ALIGN"))
),
parsePosition(attrs.getValue("POS")))

View File

@ -58,11 +58,12 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
xml.writeStartElement("BLOCK")
xml.writeAttribute("NAME", block.label)
xml.writeAttribute("ADDRESS", block.options.address?.toHex() ?: "")
xml.writeAttribute("LIBRARY", block.library.toString())
xml.writeAttribute("FORCEOUTPUT", block.options.forceOutput.toString())
xml.writeAttribute("NOPREFIXING", block.options.noSymbolPrefixing.toString())
xml.writeAttribute("VERAFXMULS", block.options.veraFxMuls.toString())
if(block.options.forceOutput) xml.writeAttribute("FORCEOUTPUT", "true")
if(block.options.noSymbolPrefixing) xml.writeAttribute("NOPREFIXING", "true")
if(block.options.veraFxMuls) xml.writeAttribute("VERAFXMULS", "true")
if(block.options.ignoreUnused) xml.writeAttribute("IGNOREUNUSED", "true")
xml.writeAttribute("ALIGN", block.options.alignment.toString())
xml.writeAttribute("LIBRARY", block.library.toString())
xml.writeAttribute("POS", block.position.toString())
xml.writeCharacters("\n")
block.children.forEach { child ->

View File

@ -361,6 +361,7 @@ class IRBlock(
val forceOutput: Boolean = false,
val noSymbolPrefixing: Boolean = false,
val veraFxMuls: Boolean = false,
val ignoreUnused: Boolean = false,
val alignment: BlockAlignment = BlockAlignment.NONE)
operator fun plusAssign(sub: IRSubroutine) { children += sub }

View File

@ -34,6 +34,8 @@ INVALID_AND_COMPOSITE: '&&' ;
fragment HEX_DIGIT: ('a'..'f') | ('A'..'F') | ('0'..'9') ;
fragment BIN_DIGIT: ('0' | '1') ;
fragment DEC_DIGIT: ('0'..'9') ;
fragment LOWERCASE: ('a'..'z') ;
DIRECTIVE: '%' LOWERCASE+ ;
FLOAT_NUMBER : FNUMBER (('E'|'e') ('+' | '-')? DEC_INTEGER)? ; // sign comes later from unary expression
FNUMBER : FDOTNUMBER | FNUMDOTNUMBER ;
@ -128,11 +130,7 @@ labeldef : identifier ':' ;
unconditionaljump : 'goto' (integerliteral | scoped_identifier) ;
directive :
directivename=('%output' | '%launcher' | '%zeropage' | '%zpreserved' | '%zpallowed' | '%address' | '%import' |
'%breakpoint' | '%asminclude' | '%asmbinary' | '%option' | '%encoding' )
(directivearg? | directivearg (',' directivearg)*)
;
directive : DIRECTIVE (directivearg? | directivearg (',' directivearg)*) ;
directivearg : stringliteral | identifier | integerliteral ;