diff --git a/src/main/java/dk/camelot64/kickc/model/VariableBuilder.java b/src/main/java/dk/camelot64/kickc/model/VariableBuilder.java index 0b6b89b59..1c6aacfd3 100644 --- a/src/main/java/dk/camelot64/kickc/model/VariableBuilder.java +++ b/src/main/java/dk/camelot64/kickc/model/VariableBuilder.java @@ -1,6 +1,5 @@ package dk.camelot64.kickc.model; -import dk.camelot64.kickc.CompileLog; import dk.camelot64.kickc.model.symbols.*; import dk.camelot64.kickc.model.types.SymbolType; import dk.camelot64.kickc.model.types.SymbolTypePointer; @@ -51,24 +50,6 @@ public class VariableBuilder { this.config = config; } - public static VariableBuilderConfig getDefaultConfig(CompileLog log) { - VariableBuilderConfig config = new VariableBuilderConfig(); - - //config.addSetting("ssa_mem", log, null); - //config.addSetting("pointer_ssa_zp", log, null); - //config.addSetting("array_ma_mem", log, null); - //config.addSetting("global_struct_ma_mem", log, null); - - //config.addSetting("ma_mem", log, null); - //config.addSetting("pointer_ma_zp", log, null); - //config.addSetting("parameter_ssa_mem", log, null); - - config.addSetting("ssa_zp", log, null); - config.addSetting("array_ma_mem", log, null); - config.addSetting("global_struct_ma_mem", log, null); - return config; - } - /** * Build the variable with the properties derived from type, scope, directives and configuration. * diff --git a/src/main/java/dk/camelot64/kickc/model/VariableBuilderConfig.java b/src/main/java/dk/camelot64/kickc/model/VariableBuilderConfig.java index 6aa2324d6..691e52587 100644 --- a/src/main/java/dk/camelot64/kickc/model/VariableBuilderConfig.java +++ b/src/main/java/dk/camelot64/kickc/model/VariableBuilderConfig.java @@ -10,20 +10,70 @@ import java.util.*; * Holds settings specified using #pragma var_model(...) * The parameters to the pragma has the form scope_type_optimization_memoryarea. * *

* For instance the parameter local_pointer_ssa_zp specifies that local pointer variables must be SSA-optimized and placed on zeropage. + * Multiple parameters can be added to the pragma to apply settings for many types/scopes. All applied parameters are processed in order potentially overwriting each other. + *

* The scope or type sub-element of the pragma parameter can be left out to apply to all scopes/types. - * For instance the parameter pointer_ssa_zp specifies that all pointer variables regardless of scope must be SSA-optimized and placed on zeropage. - *

- * Multiple parameters can be added to the pragma to apply settings for many types/scopes. + * For instance the parameter integer_ssa_mem specifies that all integer variables regardless of scope must be SSA-optimized and placed in main memory. + *

+ * Optimization or memoryarea can also be left out to only change that one setting for the scope/type combinations. + * For instance the parameter parameter_ssa specifies that all parameters must be single-static-assignment but does not specify what memory area they should be placed in. + *

+ * If the very first parameter is full no default settings are applied. This means the parameters must configure all scopes/types. + * If the first parameter is not full then default settings are automatically added before and after the settings passed as parameters. + * The default settings are + *

+ *

*/ public class VariableBuilderConfig { + /** Setting specifying that the Variable Builder config is "full" and the default pre/post should not be applied. */ + public static final String SETTING_FULL = "full"; + + /** + * Apply any default pre configuration of the variable builder configuration. + * Done as the first step when initializing a variable builder configuration + * @param config The variable builder configuration + * @param log The compile log + */ + public static void defaultPreConfig(VariableBuilderConfig config, CompileLog log) { + config.addSetting("ssa_zp", log, StatementSource.NONE); + } + + /** + * Apply any default post configuration of the variable builder configuration. + * Done as the last step when initializing a variable builder configuration + * @param config The variable builder configuration + * @param log The compile log + */ + public static void defaultPostConfig(VariableBuilderConfig config, CompileLog log) { + // Arrays are always load/store variables in main memory + // TODO: Theoretically some program may want an array on ZP. How to support that? + config.addSetting("array_ma_mem", log, StatementSource.NONE); + // Global struct values are always load/store variables in main memory + // TODO: Global structs can be SSA (and then unwound) which can optimize some programs. How to support that? + config.addSetting("global_struct_ma_mem", log, StatementSource.NONE); + // Parameters are always passed using single-static-assignment + // TODO: Compilation Unit support will require parameters that are not SSA. How to specify that? + config.addSetting("parameter_ssa", log, StatementSource.NONE); + // Pointers are always on zeropage + // TODO: Pointers can technically exist in main-memory and be moved to ZP on every use. How to specify that? + config.addSetting("pointer_zp", log, StatementSource.NONE); + } + /** The different scopes. */ public enum Scope { LOCAL, GLOBAL, PARAMETER, MEMBER @@ -89,7 +139,7 @@ public class VariableBuilderConfig { */ private Map settings; - VariableBuilderConfig() { + public VariableBuilderConfig() { this.settings = new HashMap<>(); } @@ -99,10 +149,14 @@ public class VariableBuilderConfig { List types = getTypes(paramElements); Optimization optimization = getOptimization(paramElements); MemoryArea memoryArea = getMemoryArea(paramElements); - if(memoryArea == null || optimization == null || paramElements.size() > 0) + if((memoryArea == null && optimization == null) || paramElements.size() > 0) throw new CompileError("Warning: Malformed var_model parameter " + pragmaParam, statementSource); for(Scope scope : scopes) { for(Type type : types) { + if(memoryArea == null) + memoryArea = getSetting(scope, type).memoryArea; + if(optimization == null) + optimization = getSetting(scope, type).optimization; settings.put(new ScopeType(scope, type), new Setting(scope, type, memoryArea, optimization)); } } @@ -166,6 +220,8 @@ public class VariableBuilderConfig { * @return The matched memory area. */ private MemoryArea getMemoryArea(List paramElements) { + if(paramElements.size() == 0) + return null; final String paramElement = paramElements.get(0); if(paramElement.equals("mem")) { paramElements.remove(0); @@ -185,6 +241,8 @@ public class VariableBuilderConfig { * @return The matched optimization. */ private Optimization getOptimization(List paramElements) { + if(paramElements.size() == 0) + return null; final String paramElement = paramElements.get(0); if(paramElement.equals("ssa")) { paramElements.remove(0); diff --git a/src/main/java/dk/camelot64/kickc/model/statements/StatementSource.java b/src/main/java/dk/camelot64/kickc/model/statements/StatementSource.java index ac2a3a686..adffa8e7d 100644 --- a/src/main/java/dk/camelot64/kickc/model/statements/StatementSource.java +++ b/src/main/java/dk/camelot64/kickc/model/statements/StatementSource.java @@ -33,6 +33,9 @@ public class StatementSource implements Serializable { this.stopIndex = stopIndex; } + /** An empty statement source. */ + public static StatementSource NONE = new StatementSource(null, null, null, 0, 0); + public StatementSource(Token tokenStart, Token tokenStop) { if(tokenStart != null) { this.startIndex = tokenStart.getStartIndex(); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index a33431d2e..8f118bbbb 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -58,7 +58,10 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor(); this.defaultMemoryArea = Variable.MemoryArea.ZEROPAGE_MEMORY; - this.variableBuilderConfig = VariableBuilder.getDefaultConfig(program.getLog()); + VariableBuilderConfig config = new VariableBuilderConfig(); + VariableBuilderConfig.defaultPreConfig(config, program.getLog()); + VariableBuilderConfig.defaultPostConfig(config, program.getLog()); + this.variableBuilderConfig = config; scopeStack.push(program.getScope()); } @@ -104,10 +107,22 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor settings = new ArrayList<>(ctx.NAME()); + // Detect if the first setting is "full" + boolean full = false; + if(settings.size() > 0 && settings.get(0).getText().equals(VariableBuilderConfig.SETTING_FULL)) { + full = true; + settings = settings.subList(1, settings.size()); } + VariableBuilderConfig config = new VariableBuilderConfig(); + if(!full) + VariableBuilderConfig.defaultPreConfig(config, program.getLog()); + for(TerminalNode varModel : settings) { + config.addSetting(varModel.getText(), program.getLog(), new StatementSource(ctx)); + } + if(!full) + VariableBuilderConfig.defaultPostConfig(config, program.getLog()); + this.variableBuilderConfig = config; return null; } @@ -583,16 +598,16 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor