package org.simantics.scl.compiler.internal.codegen.chr; import java.util.ArrayList; import org.cojen.classfile.TypeDesc; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.simantics.scl.compiler.elaboration.chr.CHRRuleset; import org.simantics.scl.compiler.elaboration.chr.plan.PrioritizedPlan; import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint; import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint.IndexInfo; import org.simantics.scl.compiler.internal.codegen.references.BoundVar; import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator; import org.simantics.scl.compiler.internal.codegen.utils.ClassBuilder; import org.simantics.scl.compiler.internal.codegen.utils.CodeBuilderUtils; import org.simantics.scl.compiler.internal.codegen.utils.Constants; import org.simantics.scl.compiler.internal.codegen.utils.LocalVariable; import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder; import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilderBase; import org.simantics.scl.compiler.internal.codegen.utils.ModuleBuilder; import gnu.trove.list.array.TIntArrayList; import gnu.trove.set.hash.THashSet; public class CHRCodeGenerator { public static final TypeDesc FACT_ID_TYPE = TypeDesc.INT; public static final String CHRHashIndex_name = "org/simantics/scl/runtime/chr/CHRHashIndex"; public static final TypeDesc CHRHashIndex = TypeDesc.forClass(CHRHashIndex_name); public static final String FactActivationQueue_name = "org/simantics/scl/runtime/chr/FactActivationQueue"; public static final TypeDesc FactActivationQueue = TypeDesc.forClass(FactActivationQueue_name); public static final String Fact_name = "org/simantics/scl/runtime/chr/Fact"; public static final TypeDesc Fact = TypeDesc.forClass(Fact_name); public static final String QUEUE = "queue"; private static class StoreInitialization { final int access; final String fieldName; final TypeDesc fieldType; final String className; public StoreInitialization(int access, String fieldName, TypeDesc fieldType, String className) { this.access = access; this.fieldName = fieldName; this.fieldType = fieldType; this.className = className; } } public static void generateStore(ModuleBuilder moduleBuilder, CHRRuleset ruleset) { ClassBuilder storeClassBuilder = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, ruleset.storeClassName, "java/lang/Object"); if(ruleset.parameters == null) ruleset.parameters = new BoundVar[0]; ruleset.parameterTypeDescs = moduleBuilder.getJavaTypeTranslator().getTypeDescs(ruleset.parameters); ArrayList hashIndexInitializations = new ArrayList<>(); for(CHRConstraint constraint : ruleset.constraints) generateFact(storeClassBuilder, constraint, hashIndexInitializations); // Fields for(int i=0;i hashIndexInitializations) { CHRRuleset ruleset = constraint.parentRuleset; boolean supportsRemoval = constraint.mayBeRemoved(); ModuleBuilder moduleBuilder = storeClassBuilder.getModuleBuilder(); JavaTypeTranslator jtt = moduleBuilder.getJavaTypeTranslator(); TypeDesc storeTypeDesc = storeClassBuilder.getType(); TypeDesc[] storeTypeDescArray = new TypeDesc[] { storeTypeDesc }; String factClassName = storeClassBuilder.getClassName() + "$" + constraint.name; TypeDesc factTypeDesc = TypeDesc.forClass(factClassName); ClassBuilder factClassBuilder = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, factClassName, "java/lang/Object", Fact_name); // Fields /* public int id; public int c0; // key public int c1; public ExampleFact bfPrev; public ExampleFact bfNext; */ TypeDesc[] parameterTypeDescs = jtt.toTypeDescs(constraint.parameterTypes); factClassBuilder.addField(Opcodes.ACC_PUBLIC, "id", FACT_ID_TYPE); for(int i=0;i getParameterTypeDescs = new ArrayList(constraint.parameterTypes.length); for(int i=0;i>i)&1)==1) getParameterTypeDescs.add(parameterTypeDescs[i]); MethodBuilderBase mb = storeClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, constraint.name + "$" + indexInfo.indexName, factTypeDesc, getParameterTypeDescs.toArray(new TypeDesc[getParameterTypeDescs.size()])); mb.loadThis(); mb.loadField(storeClassBuilder.getClassName(), constraint.name + "$temp", factTypeDesc); LocalVariable tempFactVar = mb.createLocalVariable("temp", factTypeDesc); mb.storeLocal(tempFactVar); int parameterId=0; for(int i=0;i>i)&1)==1) { TypeDesc typeDesc = parameterTypeDescs[i]; if(!typeDesc.equals(TypeDesc.VOID)) { mb.loadLocal(tempFactVar); mb.loadLocal(mb.getParameter(parameterId)); mb.storeField(factClassName, fieldName(i), typeDesc); } ++parameterId; } mb.loadThis(); mb.loadField(storeClassBuilder.getClassName(), constraint.name + "$" + indexInfo.indexName, CHRHashIndex); mb.loadLocal(tempFactVar); mb.invokeVirtual(CHRHashIndex_name, supportsRemoval ? "getEqual" : "getEqualNoRemovals", TypeDesc.OBJECT, Constants.OBJECTS[1]); mb.checkCast(factTypeDesc); mb.returnValue(factTypeDesc); mb.finish(); } } } // Method: add { MethodBuilderBase mb = factClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "add", TypeDesc.VOID, storeTypeDescArray); LocalVariable storeParameter = mb.getParameter(0); for(IndexInfo indexInfo : constraint.getIndices()) { String linkedListPrev = indexInfo.indexName + "Prev"; String linkedListNext = indexInfo.indexName + "Next"; String storeHashIndexName = constraint.name + "$" + indexInfo.indexName; // public void add(ExampleStore store) { // bfNext = (ExampleFact)store.ExampleFact_bfIndex.addFreshAndReturnOld(this); // if(bfNext != null) // bfNext.bfPrev = this; // } if(indexInfo.indexMask == 0) { mb.loadThis(); mb.loadLocal(storeParameter); mb.loadField(storeClassBuilder.getClassName(), storeHashIndexName, factTypeDesc); if(supportsRemoval) mb.dupX1(); mb.storeField(factClassName, linkedListNext, factTypeDesc); if(supportsRemoval) { Label cont = new Label(); mb.ifNullBranch(cont, true); mb.loadThis(); mb.loadField(factClassName, linkedListNext, factTypeDesc); mb.loadThis(); mb.storeField(factClassName, linkedListPrev, factTypeDesc); mb.setLocation(cont); } mb.loadLocal(storeParameter); mb.loadThis(); mb.storeField(storeClassBuilder.getClassName(), storeHashIndexName, factTypeDesc); } else { // bfNext = (ExampleFact)store.ExampleFact_bfIndex.addFreshAndReturnOld(this); mb.loadThis(); mb.loadLocal(storeParameter); mb.loadField(storeClassBuilder.getClassName(), storeHashIndexName, CHRHashIndex); mb.loadThis(); mb.invokeVirtual(CHRHashIndex_name, supportsRemoval ? "addFreshAndReturnOld" : "addFreshAndReturnOld", TypeDesc.OBJECT, Constants.OBJECTS[1]); mb.checkCast(factTypeDesc); if(supportsRemoval) mb.dupX1(); mb.storeField(factClassName, linkedListNext, factTypeDesc); // leaves bfNext on the stack //if(bfNext != null) // bfNext.bfPrev = this; if(supportsRemoval) { Label cont = new Label(); mb.ifNullBranch(cont, true); mb.loadThis(); mb.loadField(factClassName, linkedListNext, factTypeDesc); mb.loadThis(); mb.storeField(factClassName, linkedListPrev, factTypeDesc); mb.setLocation(cont); } } } if(!constraint.isPassive()) { mb.loadLocal(storeParameter); mb.loadField(storeClassBuilder.getClassName(), QUEUE, FactActivationQueue); mb.loadConstant(constraint.getMinimumPriority()); mb.loadThis(); mb.invokeVirtual(FactActivationQueue_name, "add", TypeDesc.VOID, new TypeDesc[] {TypeDesc.INT, Fact}); } mb.returnVoid(); mb.finish(); } // Method: remove if(supportsRemoval) { // public void remove(ExampleStore store) { // if(bfPrev == null) { // if(bfNext == null) // store.ExampleFact_bfIndex.removeKnownToExistKey(this); // else { // bfNext.bfPrev = null; // store.ExampleFact_bfIndex.replaceKnownToExistKey(this, bfNext); // } // } // else { // bfPrev.bfNext = bfNext; // if(bfNext != null) // bfNext.bfPrev = bfPrev; // } // } MethodBuilderBase mb = factClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "remove", TypeDesc.VOID, storeTypeDescArray); LocalVariable storeParameter = mb.getParameter(0); for(IndexInfo indexInfo : constraint.getIndices()) { String linkedListPrev = indexInfo.indexName + "Prev"; String linkedListNext = indexInfo.indexName + "Next"; String storeHashIndexName = constraint.name + "$" + indexInfo.indexName; Label nextIndex = mb.createLabel(); // if(bfPrev == null) { mb.loadThis(); mb.loadField(factClassName, linkedListPrev, factTypeDesc); Label else1 = new Label(); mb.ifNullBranch(else1, false); // if(bfNext == null) mb.loadThis(); mb.loadField(factClassName, linkedListNext, factTypeDesc); Label else2 = new Label(); mb.ifNullBranch(else2, false); // store.ExampleFact_bfIndex.removeKnownToExistKey(this); if(indexInfo.indexMask == 0) { mb.loadLocal(storeParameter); mb.loadNull(); mb.storeField(storeClassBuilder.getClassName(), storeHashIndexName, factTypeDesc); } else { mb.loadLocal(storeParameter); mb.loadField(storeClassBuilder.getClassName(), storeHashIndexName, CHRHashIndex); mb.loadThis(); mb.invokeVirtual(CHRHashIndex_name, "removeKnownToExistKey", TypeDesc.VOID, Constants.OBJECTS[1]); } mb.branch(nextIndex); // else { mb.setLocation(else2); // bfNext.bfPrev = null; mb.loadThis(); mb.loadField(factClassName, linkedListNext, factTypeDesc); mb.loadNull(); mb.storeField(factClassName, linkedListPrev, factTypeDesc); // store.ExampleFact_bfIndex.replaceKnownToExistKey(this, bfNext); if(indexInfo.indexMask == 0) { mb.loadLocal(storeParameter); mb.loadThis(); mb.loadField(factClassName, linkedListNext, factTypeDesc); mb.storeField(storeClassBuilder.getClassName(), storeHashIndexName, factTypeDesc); } else { mb.loadLocal(storeParameter); mb.loadField(storeClassBuilder.getClassName(), storeHashIndexName, CHRHashIndex); mb.loadThis(); mb.loadThis(); mb.loadField(factClassName, linkedListNext, factTypeDesc); mb.invokeVirtual(CHRHashIndex_name, "replaceKnownToExistKey", TypeDesc.VOID, Constants.OBJECTS[2]); } mb.branch(nextIndex); // } // else { mb.setLocation(else1); // bfPrev.bfNext = bfNext; mb.loadThis(); mb.loadField(factClassName, linkedListPrev, factTypeDesc); mb.loadThis(); mb.loadField(factClassName, linkedListNext, factTypeDesc); mb.storeField(factClassName, linkedListNext, factTypeDesc); // if(bfNext != null) mb.loadThis(); mb.loadField(factClassName, linkedListNext, factTypeDesc); Label else3 = new Label(); mb.ifNullBranch(else3, true); // bfNext.bfPrev = bfPrev; mb.loadThis(); mb.loadField(factClassName, linkedListNext, factTypeDesc); mb.loadThis(); mb.loadField(factClassName, linkedListPrev, factTypeDesc); mb.storeField(factClassName, linkedListPrev, factTypeDesc); mb.setLocation(else3); mb.branch(nextIndex); // } mb.setLocation(nextIndex); } mb.loadThis(); mb.loadConstant(-1); mb.storeField(factClassName, "id", FACT_ID_TYPE); mb.returnVoid(); mb.finish(); } // Method: isAlive { // @Override // public boolean isAlive() { // return id >= 0; // } MethodBuilderBase mb = factClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "isAlive", TypeDesc.BOOLEAN, Constants.EMPTY_TYPEDESC_ARRAY); if(supportsRemoval) { mb.loadThis(); mb.loadField(factClassName, "id", FACT_ID_TYPE); Label thenBranch = mb.createLabel(); mb.ifZeroComparisonBranch(thenBranch, "<"); mb.loadConstant(true); mb.returnValue(TypeDesc.BOOLEAN); mb.setLocation(thenBranch); mb.loadConstant(false); mb.returnValue(TypeDesc.BOOLEAN); } else { mb.loadConstant(true); mb.returnValue(TypeDesc.BOOLEAN); } mb.finish(); } // activate parts THashSet usedParameters = new THashSet(); for(int i=0;i { if(valRef.getBinding() instanceof BoundVar) usedParameters.add((BoundVar)valRef.getBinding()); }); for(int j=0;j labels = new ArrayList