1 package org.simantics.scl.compiler.internal.codegen.chr;
3 import java.util.ArrayList;
5 import org.cojen.classfile.TypeDesc;
6 import org.objectweb.asm.Label;
7 import org.objectweb.asm.Opcodes;
8 import org.simantics.scl.compiler.elaboration.chr.CHRRuleset;
9 import org.simantics.scl.compiler.elaboration.chr.plan.PrioritizedPlan;
10 import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint;
11 import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint.IndexInfo;
12 import org.simantics.scl.compiler.internal.codegen.references.BoundVar;
13 import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator;
14 import org.simantics.scl.compiler.internal.codegen.utils.ClassBuilder;
15 import org.simantics.scl.compiler.internal.codegen.utils.CodeBuilderUtils;
16 import org.simantics.scl.compiler.internal.codegen.utils.Constants;
17 import org.simantics.scl.compiler.internal.codegen.utils.LocalVariable;
18 import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;
19 import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilderBase;
20 import org.simantics.scl.compiler.internal.codegen.utils.ModuleBuilder;
22 import gnu.trove.list.array.TIntArrayList;
23 import gnu.trove.set.hash.THashSet;
25 public class CHRFactCodeGenerator {
26 private static final TypeDesc FACT_ID_TYPE = TypeDesc.INT;
27 private static final String CHRHashIndex_name = "org/simantics/scl/runtime/chr/CHRHashIndex";
28 private static final TypeDesc CHRHashIndex = TypeDesc.forClass(CHRHashIndex_name);
29 private static final String FactActivationQueue_name = "org/simantics/scl/runtime/chr/FactActivationQueue";
30 private static final TypeDesc FactActivationQueue = TypeDesc.forClass(FactActivationQueue_name);
31 private static final String Fact_name = "org/simantics/scl/runtime/chr/Fact";
32 private static final TypeDesc Fact = TypeDesc.forClass(Fact_name);
33 private static final String QUEUE = "queue";
35 private ModuleBuilder moduleBuilder;
36 private JavaTypeTranslator jtt;
37 private CHRRuleset ruleset;
39 private ClassBuilder storeClassBuilder;
40 private CHRConstraint constraint;
42 private String factClassName;
43 private TypeDesc factTypeDesc;
44 private ClassBuilder factClassBuilder;
46 private TypeDesc storeTypeDesc;
47 private TypeDesc[] storeTypeDescArray;
49 private TypeDesc[] parameterTypeDescs;
50 private boolean supportsRemoval;
52 CHRFactCodeGenerator(ClassBuilder storeClassBuilder, CHRConstraint constraint) {
53 this.storeClassBuilder = storeClassBuilder;
54 this.constraint = constraint;
55 this.ruleset = constraint.parentRuleset;
57 this.moduleBuilder = storeClassBuilder.getModuleBuilder();
58 this.jtt = moduleBuilder.getJavaTypeTranslator();
59 this.storeTypeDesc = storeClassBuilder.getType();
60 this.storeTypeDescArray = new TypeDesc[] { storeTypeDesc };
62 this.factClassName = storeClassBuilder.getClassName() + "$" + constraint.name;
63 this.factTypeDesc = TypeDesc.forClass(factClassName);
64 this.factClassBuilder = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, factClassName, "java/lang/Object", Fact_name);
66 this.parameterTypeDescs = jtt.toTypeDescs(constraint.parameterTypes);
67 this.supportsRemoval = constraint.mayBeRemoved();
70 public void generate(ArrayList<StoreInitialization> hashIndexInitializations) {
71 generateFields(hashIndexInitializations);
72 hashIndexInitializations.add(new StoreInitialization(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, constraint.name + "$temp", factTypeDesc, factClassName));
82 for(int i=0;i<constraint.plans.size();++i)
86 generateConstructor();
87 factClassBuilder.addDefaultConstructor();
89 moduleBuilder.addClass(factClassBuilder);
92 private void generateIndices() {
93 // public ExampleFact ExampleFact$bf(int c0) {
94 // ExampleFact$temp.c0 = c0;
95 // return (ExampleFact)ExampleFact_bfIndex.getEqual(ExampleFact$temp);
98 for(IndexInfo indexInfo : constraint.getIndices()) {
99 if(indexInfo.indexMask != 0) {
100 ArrayList<TypeDesc> getParameterTypeDescs = new ArrayList<TypeDesc>(constraint.parameterTypes.length);
101 for(int i=0;i<constraint.parameterTypes.length;++i)
102 if(((indexInfo.indexMask>>i)&1)==1)
103 getParameterTypeDescs.add(parameterTypeDescs[i]);
104 MethodBuilderBase mb = storeClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, constraint.name + "$" + indexInfo.indexName, factTypeDesc,
105 getParameterTypeDescs.toArray(new TypeDesc[getParameterTypeDescs.size()]));
107 // ExampleFact$temp.c0 = c0;
109 mb.loadField(storeClassBuilder.getClassName(), constraint.name + "$temp", factTypeDesc);
110 LocalVariable tempFactVar = mb.createLocalVariable("temp", factTypeDesc);
111 mb.storeLocal(tempFactVar);
113 for(int i=0;i<constraint.parameterTypes.length;++i)
114 if(((indexInfo.indexMask>>i)&1)==1) {
115 TypeDesc typeDesc = parameterTypeDescs[i];
116 if(!typeDesc.equals(TypeDesc.VOID)) {
117 mb.loadLocal(tempFactVar);
118 mb.loadLocal(mb.getParameter(parameterId));
119 mb.storeField(factClassName, fieldName(i), typeDesc);
124 // return (ExampleFact)ExampleFact_bfIndex.getEqual(ExampleFact$temp);
126 mb.loadField(storeClassBuilder.getClassName(), constraint.name + "$" + indexInfo.indexName, CHRHashIndex);
127 mb.loadLocal(tempFactVar);
128 mb.invokeVirtual(CHRHashIndex_name, supportsRemoval ? "getEqual" : "getEqualNoRemovals", TypeDesc.OBJECT, Constants.OBJECTS[1]);
129 mb.checkCast(factTypeDesc);
130 mb.returnValue(factTypeDesc);
136 private THashSet<BoundVar> usedParameters = new THashSet<BoundVar>();
138 private void generateActivateI(int i) {
139 PrioritizedPlan plan = constraint.plans.get(i);
140 MethodBuilder mb = factClassBuilder.addMethod(Opcodes.ACC_PUBLIC, "activate" + i, TypeDesc.BOOLEAN, storeTypeDescArray);
141 LocalVariable storeVar = mb.getParameter(0);
142 LocalVariable factVar = new LocalVariable(0, factTypeDesc);
143 mb.setLocalVariable(ruleset.this_, storeVar);
144 mb.setLocalVariable(plan.implementation.getParameters()[0], factVar);
146 // Set closure parameters
147 usedParameters.clear();
148 plan.implementation.forValRefs(valRef -> {
149 if(valRef.getBinding() instanceof BoundVar)
150 usedParameters.add((BoundVar)valRef.getBinding());
152 for(int j=0;j<ruleset.parameters.length;++j) {
153 BoundVar parameter = ruleset.parameters[j];
154 if(!usedParameters.contains(parameter))
156 mb.loadLocal(storeVar);
157 mb.loadField(storeClassBuilder.getClassName(), "p"+j, ruleset.parameterTypeDescs[j]);
162 //System.out.println("=== activate" + i + " ==========================================================");
163 //System.out.println(plan.implementation);
164 plan.implementation.markGenerateOnFly();
165 plan.implementation.generateCodeWithAlreadyPreparedParameters(mb);
169 private void generateActivate() {
171 // public int activate(Object context, int priority) {
175 MethodBuilderBase mb = factClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "activate", TypeDesc.INT, new TypeDesc[] {TypeDesc.OBJECT, TypeDesc.INT});
176 Label defaultLabel = mb.createLabel();
178 if(!constraint.isPassive()) {
179 // Check if the fact is alive
181 mb.loadField(factClassName, "id", TypeDesc.INT);
182 mb.ifZeroComparisonBranch(defaultLabel, "<");
184 mb.loadLocal(mb.getParameter(0));
185 mb.checkCast(storeTypeDesc);
186 LocalVariable storeVariable = new LocalVariable(1, storeTypeDesc);
187 mb.storeLocal(storeVariable);
189 TIntArrayList priorities = new TIntArrayList(constraint.plans.size());
190 ArrayList<Label> labels = new ArrayList<Label>();
191 int lastPriority = -1;
192 for(PrioritizedPlan plan : constraint.plans)
193 if(plan.priority != lastPriority) {
194 priorities.add(plan.priority);
195 labels.add(mb.createLabel());
196 lastPriority = plan.priority;
199 mb.loadLocal(mb.getParameter(1));
200 mb.switch_(priorities.toArray(), labels.toArray(new Label[labels.size()]), defaultLabel);
202 for(int i=0;i<constraint.plans.size();++i) {
203 PrioritizedPlan plan = constraint.plans.get(i);
204 if(labelId == -1 || plan.priority != priorities.get(labelId)) {
206 mb.loadConstant(plan.priority);
207 mb.returnValue(TypeDesc.INT);
210 mb.setLocation(labels.get(labelId));
213 mb.loadLocal(storeVariable);
214 mb.invokeVirtual(factClassName, "activate" + i, TypeDesc.BOOLEAN, new TypeDesc[] {storeTypeDesc});
215 mb.ifZeroComparisonBranch(defaultLabel, "==");
217 mb.setLocation(defaultLabel);
220 mb.returnValue(TypeDesc.INT);
224 private void generateConstructor() {
225 // public ExampleFact(int id, int c0, int c1) {
231 ArrayList<TypeDesc> constructorParameters = new ArrayList<TypeDesc>(parameterTypeDescs.length+1);
232 constructorParameters.add(FACT_ID_TYPE);
233 for(TypeDesc typeDesc : parameterTypeDescs) {
234 if(typeDesc.equals(TypeDesc.VOID))
236 constructorParameters.add(typeDesc);
238 MethodBuilderBase mb = factClassBuilder.addConstructor(Opcodes.ACC_PUBLIC, constructorParameters.toArray(new TypeDesc[constructorParameters.size()]));
240 mb.invokeConstructor(factClassBuilder.getSuperClassName(), Constants.EMPTY_TYPEDESC_ARRAY);
242 mb.loadLocal(mb.getParameter(0));
243 mb.storeField(factClassName, "id", FACT_ID_TYPE);
244 for(int i=0,parameterId=1;i<constraint.parameterTypes.length;++i) {
245 TypeDesc typeDesc = parameterTypeDescs[i];
246 if(typeDesc.equals(TypeDesc.VOID))
249 mb.loadLocal(mb.getParameter(parameterId++));
250 mb.storeField(factClassName, fieldName(i), typeDesc);
256 private void generateIsAlive() {
258 // public boolean isAlive() {
262 MethodBuilderBase mb = factClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "isAlive", TypeDesc.BOOLEAN, Constants.EMPTY_TYPEDESC_ARRAY);
263 if(supportsRemoval) {
265 mb.loadField(factClassName, "id", FACT_ID_TYPE);
267 Label thenBranch = mb.createLabel();
268 mb.ifZeroComparisonBranch(thenBranch, "<");
269 mb.loadConstant(true);
270 mb.returnValue(TypeDesc.BOOLEAN);
272 mb.setLocation(thenBranch);
273 mb.loadConstant(false);
274 mb.returnValue(TypeDesc.BOOLEAN);
277 mb.loadConstant(true);
278 mb.returnValue(TypeDesc.BOOLEAN);
283 private void generateAdd() {
284 MethodBuilderBase mb = factClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "add", TypeDesc.VOID, storeTypeDescArray);
285 LocalVariable storeParameter = mb.getParameter(0);
286 for(IndexInfo indexInfo : constraint.getIndices()) {
287 String linkedListPrev = indexInfo.indexName + "Prev";
288 String linkedListNext = indexInfo.indexName + "Next";
289 String storeHashIndexName = constraint.name + "$" + indexInfo.indexName;
291 // public void add(ExampleStore store) {
292 // bfNext = (ExampleFact)store.ExampleFact_bfIndex.addFreshAndReturnOld(this);
293 // if(bfNext != null)
294 // bfNext.bfPrev = this;
297 if(indexInfo.indexMask == 0) {
299 mb.loadLocal(storeParameter);
300 mb.loadField(storeClassBuilder.getClassName(), storeHashIndexName, factTypeDesc);
303 mb.storeField(factClassName, linkedListNext, factTypeDesc);
304 if(supportsRemoval) {
305 Label cont = new Label();
306 mb.ifNullBranch(cont, true);
308 mb.loadField(factClassName, linkedListNext, factTypeDesc);
310 mb.storeField(factClassName, linkedListPrev, factTypeDesc);
311 mb.setLocation(cont);
313 mb.loadLocal(storeParameter);
315 mb.storeField(storeClassBuilder.getClassName(), storeHashIndexName, factTypeDesc);
318 // bfNext = (ExampleFact)store.ExampleFact_bfIndex.addFreshAndReturnOld(this);
320 mb.loadLocal(storeParameter);
321 mb.loadField(storeClassBuilder.getClassName(), storeHashIndexName, CHRHashIndex);
323 mb.invokeVirtual(CHRHashIndex_name, supportsRemoval ? "addFreshAndReturnOld" : "addFreshAndReturnOld", TypeDesc.OBJECT, Constants.OBJECTS[1]);
324 mb.checkCast(factTypeDesc);
327 mb.storeField(factClassName, linkedListNext, factTypeDesc);
328 // leaves bfNext on the stack
331 // bfNext.bfPrev = this;
332 if(supportsRemoval) {
333 Label cont = new Label();
334 mb.ifNullBranch(cont, true);
336 mb.loadField(factClassName, linkedListNext, factTypeDesc);
338 mb.storeField(factClassName, linkedListPrev, factTypeDesc);
339 mb.setLocation(cont);
343 if(!constraint.isPassive()) {
344 mb.loadLocal(storeParameter);
345 mb.loadField(storeClassBuilder.getClassName(), QUEUE, FactActivationQueue);
346 mb.loadConstant(constraint.getMinimumPriority());
348 mb.invokeVirtual(FactActivationQueue_name, "add", TypeDesc.VOID, new TypeDesc[] {TypeDesc.INT, Fact});
354 private void generateFields(ArrayList<StoreInitialization> hashIndexInitializations) {
356 // public int c0; // key
358 // public ExampleFact bfPrev;
359 // public ExampleFact bfNext;
361 factClassBuilder.addField(Opcodes.ACC_PUBLIC, "id", FACT_ID_TYPE);
362 for(int i=0;i<constraint.parameterTypes.length;++i) {
363 TypeDesc typeDesc = parameterTypeDescs[i];
364 if(typeDesc.equals(TypeDesc.VOID))
366 if(parameterTypeDescs[i] != TypeDesc.VOID)
367 factClassBuilder.addField(Opcodes.ACC_PUBLIC, fieldName(i), typeDesc);
370 for(IndexInfo indexInfo : constraint.getIndices()) {
372 factClassBuilder.addField(Opcodes.ACC_PUBLIC, indexInfo.indexName + "Prev", factTypeDesc);
373 factClassBuilder.addField(Opcodes.ACC_PUBLIC, indexInfo.indexName + "Next", factTypeDesc);
375 String hashIndexField = constraint.name + "$" + indexInfo.indexName;
376 if(indexInfo.indexMask == 0) {
377 // If there are no bound parameters, use just a direct reference to a fact
378 storeClassBuilder.addField(Opcodes.ACC_PUBLIC, hashIndexField, factTypeDesc);
381 ClassBuilder hashClass = generateSpecializedHashIndex(storeClassBuilder, constraint, indexInfo, factTypeDesc, factClassName);
382 moduleBuilder.addClass(hashClass);
383 hashIndexInitializations.add(new StoreInitialization(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, hashIndexField, CHRHashIndex, hashClass.getClassName()));
388 private void generateRemove() {
389 // public void remove(ExampleStore store) {
390 // if(bfPrev == null) {
391 // if(bfNext == null)
392 // store.ExampleFact_bfIndex.removeKnownToExistKey(this);
394 // bfNext.bfPrev = null;
395 // store.ExampleFact_bfIndex.replaceKnownToExistKey(this, bfNext);
399 // bfPrev.bfNext = bfNext;
400 // if(bfNext != null)
401 // bfNext.bfPrev = bfPrev;
405 MethodBuilderBase mb = factClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "remove", TypeDesc.VOID, storeTypeDescArray);
406 LocalVariable storeParameter = mb.getParameter(0);
407 for(IndexInfo indexInfo : constraint.getIndices()) {
408 String linkedListPrev = indexInfo.indexName + "Prev";
409 String linkedListNext = indexInfo.indexName + "Next";
410 String storeHashIndexName = constraint.name + "$" + indexInfo.indexName;
412 Label nextIndex = mb.createLabel();
414 // if(bfPrev == null) {
416 mb.loadField(factClassName, linkedListPrev, factTypeDesc);
417 Label else1 = new Label();
418 mb.ifNullBranch(else1, false);
420 // if(bfNext == null)
422 mb.loadField(factClassName, linkedListNext, factTypeDesc);
423 Label else2 = new Label();
424 mb.ifNullBranch(else2, false);
426 // store.ExampleFact_bfIndex.removeKnownToExistKey(this);
427 if(indexInfo.indexMask == 0) {
428 mb.loadLocal(storeParameter);
430 mb.storeField(storeClassBuilder.getClassName(), storeHashIndexName, factTypeDesc);
433 mb.loadLocal(storeParameter);
434 mb.loadField(storeClassBuilder.getClassName(), storeHashIndexName, CHRHashIndex);
436 mb.invokeVirtual(CHRHashIndex_name, "removeKnownToExistKey", TypeDesc.VOID, Constants.OBJECTS[1]);
438 mb.branch(nextIndex);
441 mb.setLocation(else2);
442 // bfNext.bfPrev = null;
444 mb.loadField(factClassName, linkedListNext, factTypeDesc);
446 mb.storeField(factClassName, linkedListPrev, factTypeDesc);
447 // store.ExampleFact_bfIndex.replaceKnownToExistKey(this, bfNext);
448 if(indexInfo.indexMask == 0) {
449 mb.loadLocal(storeParameter);
451 mb.loadField(factClassName, linkedListNext, factTypeDesc);
452 mb.storeField(storeClassBuilder.getClassName(), storeHashIndexName, factTypeDesc);
455 mb.loadLocal(storeParameter);
456 mb.loadField(storeClassBuilder.getClassName(), storeHashIndexName, CHRHashIndex);
459 mb.loadField(factClassName, linkedListNext, factTypeDesc);
460 mb.invokeVirtual(CHRHashIndex_name, "replaceKnownToExistKey", TypeDesc.VOID, Constants.OBJECTS[2]);
462 mb.branch(nextIndex);
466 mb.setLocation(else1);
467 // bfPrev.bfNext = bfNext;
469 mb.loadField(factClassName, linkedListPrev, factTypeDesc);
471 mb.loadField(factClassName, linkedListNext, factTypeDesc);
472 mb.storeField(factClassName, linkedListNext, factTypeDesc);
473 // if(bfNext != null)
475 mb.loadField(factClassName, linkedListNext, factTypeDesc);
476 Label else3 = new Label();
477 mb.ifNullBranch(else3, true);
478 // bfNext.bfPrev = bfPrev;
480 mb.loadField(factClassName, linkedListNext, factTypeDesc);
482 mb.loadField(factClassName, linkedListPrev, factTypeDesc);
483 mb.storeField(factClassName, linkedListPrev, factTypeDesc);
484 mb.setLocation(else3);
485 mb.branch(nextIndex);
488 mb.setLocation(nextIndex);
492 mb.storeField(factClassName, "id", FACT_ID_TYPE);
497 public static String fieldName(int id) {
501 private static ClassBuilder generateSpecializedHashIndex(ClassBuilder storeClassBuilder, CHRConstraint constraint, IndexInfo indexInfo, TypeDesc factClassTypeDesc, String factClassName) {
502 // new CHRHashIndex() {
504 // protected boolean keyEquals(Object a, Object b) {
505 // return ((ExampleFact)a).c0 == ((ExampleFact)b).c0;
508 // protected int keyHashCode(Object key) {
509 // return ((ExampleFact)key).c0;
513 ModuleBuilder moduleBuilder = storeClassBuilder.getModuleBuilder();
514 JavaTypeTranslator jtt = moduleBuilder.getJavaTypeTranslator();
516 String hashIndexClassName = factClassName + "$" + indexInfo.indexName;
517 ClassBuilder hashIndexClassBuilder = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, hashIndexClassName, "org/simantics/scl/runtime/chr/CHRHashIndex");
524 // protected boolean keyEquals(Object a, Object b) {
525 // return ((ExampleFact)a).c0 == ((ExampleFact)b).c0;
528 MethodBuilderBase mb = hashIndexClassBuilder.addMethodBase(Opcodes.ACC_PROTECTED, "keyEquals", TypeDesc.BOOLEAN, Constants.OBJECTS[2]);
529 mb.loadLocal(mb.getParameter(0));
530 mb.checkCast(factClassTypeDesc);
531 LocalVariable aVar = mb.createLocalVariable("a", factClassTypeDesc);
534 mb.loadLocal(mb.getParameter(1));
535 mb.checkCast(factClassTypeDesc);
536 LocalVariable bVar = mb.createLocalVariable("b", factClassTypeDesc);
539 Label failure = mb.createLabel();
541 int curMask = indexInfo.indexMask;
542 for(int i=0;i<constraint.parameterTypes.length;++i,curMask>>=1)
543 if((curMask&1) == 1) {
544 TypeDesc fieldTypeDesc = jtt.toTypeDesc(constraint.parameterTypes[i]);
545 if(fieldTypeDesc.equals(TypeDesc.VOID))
548 mb.loadField(factClassName, fieldName(i), fieldTypeDesc);
551 mb.loadField(factClassName, fieldName(i), fieldTypeDesc);
553 CodeBuilderUtils.equals(mb, fieldTypeDesc, failure);
555 mb.loadConstant(true);
556 mb.returnValue(TypeDesc.BOOLEAN);
558 mb.setLocation(failure);
559 mb.loadConstant(false);
560 mb.returnValue(TypeDesc.BOOLEAN);
564 // Method: keyHashCode
568 // protected int keyHashCode(Object key) {
569 // return (0x811C9DC5^((ExampleFact)key).c0)*16777619;
572 MethodBuilderBase mb = hashIndexClassBuilder.addMethodBase(Opcodes.ACC_PROTECTED, "keyHashCode", TypeDesc.INT, Constants.OBJECTS[1]);
573 mb.loadLocal(mb.getParameter(0));
574 mb.checkCast(factClassTypeDesc);
575 LocalVariable factVar = mb.createLocalVariable("fact", factClassTypeDesc);
576 mb.storeLocal(factVar);
578 mb.loadConstant(0x811C9DC5);
580 int curMask = indexInfo.indexMask;
581 for(int i=0;i<constraint.parameterTypes.length;++i,curMask>>=1)
582 if((curMask&1) == 1) {
583 TypeDesc fieldTypeDesc = jtt.toTypeDesc(constraint.parameterTypes[i]);
584 if(fieldTypeDesc.equals(TypeDesc.VOID))
586 mb.loadLocal(factVar);
587 mb.loadField(factClassName, fieldName(i), fieldTypeDesc);
588 CodeBuilderUtils.hashCode(mb, fieldTypeDesc);
589 mb.math(Opcodes.IXOR);
590 mb.loadConstant(16777619);
591 mb.math(Opcodes.IMUL);
594 mb.returnValue(TypeDesc.INT);
598 hashIndexClassBuilder.addDefaultConstructor();
600 return hashIndexClassBuilder;