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.
*
- * - scope is one of global, local
+ * - scope is one of global, local or parameter
* - type is one of struct, array, integer, pointer
* - optimization is one of ma (meaning multiple-assignment or load/store), ssa (meaning single-static-assignment)
* - memoryarea is one of zp (meaning zeropage), mem (meaning main memory)
*
*
* 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
+ *
+ * - ssa_zp Applied before the passed parameters (defaulting everything to single static assignment on zeropage)
+ * - ... The passed parameters are then applied modifying the default
+ * - array_ma_mem Applied after the passed parameters (forcing arrays to load/store in main-memory)
+ * - global_struct_ma_mem Applied after the passed parameters (forcing global structs to load/store in main-memory)
+ * - parameter_ssa Applied after the passed parameters (forcing parameters to single static assignment)
+ * - pointer_zp Applied after the passed parameters (forcing pointers to zeropage)
+ *
+ *
*/
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