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