d670300cbe1a4253758ee5731a766ddc42cac0aa
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / elaboration / chr / CHRRuleset.java
1 package org.simantics.scl.compiler.elaboration.chr;
2
3 import java.util.ArrayList;
4
5 import org.cojen.classfile.TypeDesc;
6 import org.simantics.scl.compiler.compilation.CompilationContext;
7 import org.simantics.scl.compiler.constants.BooleanConstant;
8 import org.simantics.scl.compiler.constants.Constant;
9 import org.simantics.scl.compiler.constants.IntegerConstant;
10 import org.simantics.scl.compiler.constants.JavaConstructor;
11 import org.simantics.scl.compiler.constants.JavaMethod;
12 import org.simantics.scl.compiler.constants.NoRepConstant;
13 import org.simantics.scl.compiler.constants.generic.CallJava;
14 import org.simantics.scl.compiler.constants.generic.MethodRef.FieldRef;
15 import org.simantics.scl.compiler.constants.generic.MethodRef.ObjectMethodRef;
16 import org.simantics.scl.compiler.constants.generic.MethodRef.SetFieldRef;
17 import org.simantics.scl.compiler.elaboration.chr.analysis.CHRConstraintGGInfo;
18 import org.simantics.scl.compiler.elaboration.chr.analysis.UsageAnalysis;
19 import org.simantics.scl.compiler.elaboration.chr.plan.CHRSearchPlan;
20 import org.simantics.scl.compiler.elaboration.chr.plan.PlanRealizer;
21 import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint;
22 import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext;
23 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
24 import org.simantics.scl.compiler.elaboration.contexts.TypingContext;
25 import org.simantics.scl.compiler.elaboration.expressions.Variable;
26 import org.simantics.scl.compiler.elaboration.expressions.VariableProcedure;
27 import org.simantics.scl.compiler.elaboration.expressions.block.IncludeStatement;
28 import org.simantics.scl.compiler.environment.AmbiguousNameException;
29 import org.simantics.scl.compiler.errors.Locations;
30 import org.simantics.scl.compiler.internal.codegen.chr.CHRCodeGenerationConstants;
31 import org.simantics.scl.compiler.internal.codegen.chr.CHRRuntimeRulesetCodeGenerator;
32 import org.simantics.scl.compiler.internal.codegen.references.BoundVar;
33 import org.simantics.scl.compiler.internal.codegen.references.IVal;
34 import org.simantics.scl.compiler.internal.codegen.ssa.SSAFunction;
35 import org.simantics.scl.compiler.internal.codegen.types.StandardTypeConstructor;
36 import org.simantics.scl.compiler.internal.codegen.utils.Constants;
37 import org.simantics.scl.compiler.internal.codegen.writer.CodeWriter;
38 import org.simantics.scl.compiler.internal.parsing.Symbol;
39 import org.simantics.scl.compiler.types.TCon;
40 import org.simantics.scl.compiler.types.TVar;
41 import org.simantics.scl.compiler.types.Type;
42 import org.simantics.scl.compiler.types.Types;
43
44 import gnu.trove.map.hash.THashMap;
45 import gnu.trove.map.hash.TObjectIntHashMap;
46 import gnu.trove.set.hash.THashSet;
47 import gnu.trove.set.hash.TIntHashSet;
48
49 public class CHRRuleset extends Symbol {
50     
51     public static final String INIT_CONSTRAINT = "__INIT__";
52     
53     public ArrayList<CHRConstraint> constraints = new ArrayList<CHRConstraint>();
54     public ArrayList<CHRRule> rules = new ArrayList<CHRRule>();
55     public ArrayList<IncludeStatement> includes = new ArrayList<IncludeStatement>();
56     public THashMap<CHRConstraint, IncludeStatement> constraintSourceMap = new THashMap<CHRConstraint, IncludeStatement>();
57     public THashMap<CHRConstraint, CHRConstraintGGInfo> activeConstraintGGInfo = new THashMap<CHRConstraint, CHRConstraintGGInfo>(); // contains also imported constraints
58     public THashMap<IncludeStatement, ArrayList<CHRConstraint>> inverseActiveConstraintSourceMap = new THashMap<IncludeStatement, ArrayList<CHRConstraint>>();
59     
60     public boolean extensible;
61     
62     public CHRConstraint initConstraint;
63     public int priorityCount;
64     
65     public int initialPriorityNumber = 0;
66     
67     public String runtimeRulesetClassName;
68     public TCon runtimeRulesetType;
69     public BoundVar runtimeRulesetVariable;
70     public TypeDesc runtimeRulesetTypeDesc;
71     
72     public static final Constant READ_CURRENT_ID = new CallJava(TVar.EMPTY_ARRAY, Types.PROC, Types.INTEGER, new Type[] {Types.CHRContext},
73             null, new FieldRef(CHRCodeGenerationConstants.CHRContext_name, "currentId", CHRRuntimeRulesetCodeGenerator.FACT_ID_TYPE), null);
74     public static final Constant WRITE_CURRENT_ID = new CallJava(TVar.EMPTY_ARRAY, Types.PROC, Types.UNIT, new Type[] {Types.CHRContext, Types.INTEGER},
75             null, new SetFieldRef(CHRCodeGenerationConstants.CHRContext_name, "currentId", CHRRuntimeRulesetCodeGenerator.FACT_ID_TYPE), null);
76     public static final Constant GENERATE_ID = new CallJava(TVar.EMPTY_ARRAY, Types.PROC, Types.INTEGER, new Type[] {Types.CHRContext},
77             null, new ObjectMethodRef(false, CHRCodeGenerationConstants.CHRContext_name, "generateId", CHRRuntimeRulesetCodeGenerator.FACT_ID_TYPE, Constants.EMPTY_TYPEDESC_ARRAY), null);
78     
79     // FIXME remove and change the parameter of Expression.toVal
80     private CompilationContext cachedContext;
81     
82     // For code generation
83     public CHRRulesetObject rulesetObject;
84     public SSAFunction initializer;
85     public SSAFunction deinitializer;
86     
87     public CHRRuleset() {
88         initConstraint = new CHRConstraint(Locations.NO_LOCATION, INIT_CONSTRAINT, Type.EMPTY_ARRAY);
89         constraints.add(initConstraint);
90     }
91     
92     public void resolve(TranslationContext context) {
93         boolean failedIncludes = false;
94         for(IncludeStatement include : includes) {
95             try {
96                 include.ruleset = context.resolveRuleset(include.name.text);
97                 if(include.ruleset == null) {
98                     failedIncludes = true;
99                     context.getErrorLog().log(include.name.location, "Couldn't resolve ruleset " + include.name + ".");
100                     continue;
101                 }
102                 include.value = include.value.resolve(context);
103                 for(CHRConstraint constraint : include.ruleset.constraints) {
104                     context.newCHRConstraint(constraint.name, constraint);
105                     constraintSourceMap.put(constraint, include);
106                 }
107             } catch (AmbiguousNameException e) {
108                 failedIncludes = true;
109                 context.getErrorLog().log(include.name.location, e.getMessage());
110             }
111         }
112         if(failedIncludes)
113             return;
114         for(CHRConstraint constraint : constraints)
115             context.newCHRConstraint(constraint.name, constraint);
116         priorityCount = 0;
117         for(CHRRule rule : rules) {
118             rule.resolve(context);
119             rule.priority = priorityCount++;
120         }
121         /*for(CHRConstraint constraint : constraints) {
122             Variable newVariable = context.newVariable("claim" + constraint.factClassName);
123         }*/
124     }
125
126     public void collectRefs(TObjectIntHashMap<Object> allRefs, TIntHashSet refs) {
127         for(IncludeStatement include : includes)
128             include.value.collectRefs(allRefs, refs);
129         for(CHRRule rule : rules)
130             rule.collectRefs(allRefs, refs);
131     }
132
133     public void checkType(TypingContext context) {
134         for(IncludeStatement include : includes)
135             include.value = include.value.checkType(context, include.ruleset.runtimeRulesetType);
136         for(CHRRule rule : rules)
137             rule.checkType(context);
138     }
139
140     public void collectVars(TObjectIntHashMap<Variable> allVars, TIntHashSet vars) {
141         for(IncludeStatement include : includes)
142             include.value.collectVars(allVars, vars);
143         for(CHRRule rule : rules)
144             rule.collectVars(allVars, vars);
145     }
146
147     public void forVariables(VariableProcedure procedure) {
148         for(IncludeStatement include : includes)
149             include.value.forVariables(procedure);
150         for(CHRRule rule : rules)
151             rule.forVariables(procedure);
152     }
153
154     public void collectFreeVariables(THashSet<Variable> vars) {
155         for(IncludeStatement include : includes)
156             include.value.collectFreeVariables(vars);
157         for(CHRRule rule : rules)
158             rule.collectFreeVariables(vars);
159     }
160
161     public void setLocationDeep(long loc) {
162         if(location == Locations.NO_LOCATION) {
163             this.location = loc;
164             for(CHRRule rule : rules)
165                 rule.setLocationDeep(loc);
166         }
167     }
168     
169     public int getMinimumPriority(CHRConstraint constraint) {
170         CHRConstraintGGInfo info = activeConstraintGGInfo.get(constraint);
171         if(info == null)
172             return Integer.MAX_VALUE;
173         else
174             return info.minimumPriority;
175     }
176     
177     public int getAndUpdateNextPriority(CHRConstraint constraint, int nextPriority) {
178         CHRConstraintGGInfo info = activeConstraintGGInfo.get(constraint);
179         if(info == null)
180             return Integer.MAX_VALUE;
181         else {
182             int result = info.nextPriority;
183             info.nextPriority = nextPriority;
184             return result;
185         }
186     }
187
188     public void compile(SimplificationContext context) {
189         initializeCodeGeneration(context.getCompilationContext());
190         if(extensible)
191             applyExtensibleDefaults();
192         UsageAnalysis.analyzeUsage(this);
193         for(CHRRule rule : rules) {
194             rule.compile(context.getCompilationContext(), initConstraint);
195             for(CHRSearchPlan plan : rule.plans) {
196                 CHRConstraint constraint = plan.constraint;
197                 if(!activeConstraintGGInfo.containsKey(constraint)) {
198                     activeConstraintGGInfo.put(constraint, new CHRConstraintGGInfo(rule.priority));
199                     IncludeStatement include = constraintSourceMap.get(constraint);
200                     if(include != null) {
201                         ArrayList<CHRConstraint> list = inverseActiveConstraintSourceMap.get(include);
202                         if(list == null) {
203                             list = new ArrayList<CHRConstraint>(4);
204                             inverseActiveConstraintSourceMap.put(include, list);
205                         }
206                         list.add(constraint);
207                     }
208                 }
209             }
210         }
211         // remove init constraint if it is not useful
212         if(getMinimumPriority(initConstraint) == Integer.MAX_VALUE) {
213             constraints.remove(0);
214             initConstraint = null;
215         }
216     }
217
218     private void applyExtensibleDefaults() {
219         for(CHRConstraint constraint : constraints) {
220             // FIXME Too much indexing!!!
221             int max = 1 << constraint.parameterTypes.length;
222             for(int i=0;i<max;++i)
223                 constraint.getOrCreateIndex(cachedContext, i);
224             constraint.setMayBeRemoved();
225             /*
226             constraint.getOrCreateIndex(cachedContext, 0);
227             if(constraint.parameterTypes.length > 0)
228                 constraint.getOrCreateIndex(cachedContext, 1);*/
229         }
230     }
231
232     public void simplify(SimplificationContext context) {
233         for(IncludeStatement include : includes)
234             include.value = include.value.simplify(context);
235         for(CHRRule rule : rules)
236             rule.simplify(context);
237     }
238     
239     public void setRulesetType(TCon type, String className) {
240         this.runtimeRulesetType = type;
241         this.runtimeRulesetClassName = className;
242     }
243     
244     public void initializeCodeGeneration(CompilationContext context) {
245         cachedContext = context; // FIXME remove
246         
247         boolean createTypeDesc = false;
248         if(runtimeRulesetType == null) {
249             String suffix = context.namingPolicy.getFreshClosureClassNameSuffix();
250             setRulesetType(Types.con(context.namingPolicy.getModuleName(), "CHR" + suffix), context.namingPolicy.getModuleClassName() + suffix);
251             createTypeDesc = true;
252         }
253         runtimeRulesetTypeDesc = TypeDesc.forClass(runtimeRulesetClassName);
254         runtimeRulesetVariable = new BoundVar(runtimeRulesetType);
255         for(CHRConstraint constraint : constraints)
256             constraint.initializeCodeGeneration(context, this);
257         if(createTypeDesc && context.module != null) // for unit testing
258             context.module.addTypeDescriptor(runtimeRulesetType.name, new StandardTypeConstructor(runtimeRulesetType, TVar.EMPTY_ARRAY, runtimeRulesetTypeDesc));
259     }
260
261     public static final Constant ACTIVATE = new JavaMethod(true, CHRCodeGenerationConstants.CHRContext_name, "activate", Types.PROC, Types.UNIT, Types.CHRContext, Types.INTEGER);
262     private static final Constant CREATE_CHR_CONTEXT = new JavaConstructor("org/simantics/scl/runtime/chr/CHRContext", Types.PROC, Types.CHRContext);
263     
264     public IVal generateCode(CodeWriter w) {
265         for(IncludeStatement include : includes) {
266             include.storeVar = include.value.toVal(cachedContext, w);
267             initialPriorityNumber = Math.max(initialPriorityNumber, include.ruleset.initialPriorityNumber + include.ruleset.priorityCount);
268         }
269         CHRRulesetObject object = new CHRRulesetObject(runtimeRulesetVariable, this);
270         w.defineObject(object);
271         for(CHRRule rule : rules) {
272             for(CHRSearchPlan plan : rule.plans) {
273                 /*System.out.println("    plan " + plan.priority);
274                 for(PlanOp planOp : plan.ops)
275                     System.out.println("        " + planOp);*/
276                 CodeWriter methodWriter = object.createMethod(w.getModuleWriter(), TVar.EMPTY_ARRAY, Types.PROC, Types.BOOLEAN, new Type[] {Types.CHRContext, plan.constraint.factType});
277                 plan.implementation = methodWriter.getFunction();
278                 IVal[] implementationParameters = methodWriter.getParameters();
279                 plan.activeFact.setVal(implementationParameters[1]);
280                 PlanRealizer realizer = new PlanRealizer(cachedContext, this, runtimeRulesetVariable, implementationParameters[0], plan.ops);
281                 realizer.nextOp(methodWriter);
282                 if(methodWriter.isUnfinished())
283                     methodWriter.return_(BooleanConstant.TRUE);
284             }
285         }
286         if(!includes.isEmpty()) {
287             {
288                 CodeWriter methodWriter = object.createMethod(w.getModuleWriter(), TVar.EMPTY_ARRAY, Types.PROC, Types.UNIT, new Type[] {Types.CHRContext});
289                 initializer = methodWriter.getFunction();
290                 for(IncludeStatement include : includes) {
291                     methodWriter.apply(include.location,
292                             new JavaMethod(true, runtimeRulesetClassName, "register", Types.PROC, Types.UNIT, new Type[] {runtimeRulesetType, Types.CHRContext, include.ruleset.runtimeRulesetType}),
293                             object.getTarget(), methodWriter.getParameters()[0], include.storeVar);
294                 }
295                 methodWriter.return_(NoRepConstant.UNIT);
296             }
297             {
298                 CodeWriter methodWriter = object.createMethod(w.getModuleWriter(), TVar.EMPTY_ARRAY, Types.PROC, Types.UNIT, new Type[] {Types.CHRContext});
299                 deinitializer = methodWriter.getFunction();
300                 for(IncludeStatement include : includes) {
301                     methodWriter.apply(include.location,
302                             new JavaMethod(true, runtimeRulesetClassName, "unregister", Types.PROC, Types.UNIT, new Type[] {runtimeRulesetType, Types.CHRContext, include.ruleset.runtimeRulesetType}),
303                             object.getTarget(), methodWriter.getParameters()[0], include.storeVar);
304                 }
305                 methodWriter.return_(NoRepConstant.UNIT);
306             }
307         }
308         if(initConstraint != null) {
309             IVal chrContext = w.apply(location, CREATE_CHR_CONTEXT);
310             if(initializer != null) {
311                 w.apply(location,
312                         new JavaMethod(true, runtimeRulesetClassName, "initialize", Types.PROC, Types.UNIT, new Type[] {runtimeRulesetType, Types.CHRContext}),
313                         object.getTarget(), chrContext);
314             }
315             IVal initFact = w.apply(location, initConstraint.constructor, w.apply(location, GENERATE_ID, chrContext));
316             w.apply(location, initConstraint.addProcedure, runtimeRulesetVariable, chrContext, initFact);
317             w.apply(location, ACTIVATE, chrContext, new IntegerConstant(Integer.MAX_VALUE));
318             if(deinitializer != null) {
319                 w.apply(location,
320                         new JavaMethod(true, runtimeRulesetClassName, "deinitialize", Types.PROC, Types.UNIT, new Type[] {runtimeRulesetType, Types.CHRContext}),
321                         object.getTarget(), chrContext);
322             }
323         }
324         return runtimeRulesetVariable;
325     }
326
327     public void collectEffects(THashSet<Type> effects) {
328         for(CHRRule rule : rules) {
329             for(CHRLiteral literal : rule.head.literals)
330                 literal.collectQueryEffects(effects);
331             for(CHRLiteral literal : rule.head.literals)
332                 literal.collectEnforceEffects(effects);
333         }
334     }
335
336     public void addRule(CHRRule rule) {
337         rules.add(rule);
338         rule.parentRuleset = this;
339     }
340 }