package org.simantics.scl.compiler.internal.codegen.ssa; import java.util.ArrayList; import org.cojen.classfile.TypeDesc; import org.objectweb.asm.Opcodes; import org.simantics.scl.compiler.common.names.Name; import org.simantics.scl.compiler.constants.JavaStaticField; import org.simantics.scl.compiler.constants.JavaStaticMethod; import org.simantics.scl.compiler.constants.SCLConstant; import org.simantics.scl.compiler.environment.Environment; import org.simantics.scl.compiler.errors.ErrorLog; 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.CodeBuildingException; import org.simantics.scl.compiler.internal.codegen.utils.Constants; 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 org.simantics.scl.compiler.internal.codegen.utils.NameMangling; import org.simantics.scl.compiler.internal.codegen.utils.PrintingContext; import org.simantics.scl.compiler.internal.codegen.utils.SSALambdaLiftingContext; import org.simantics.scl.compiler.internal.codegen.utils.SSASimplificationContext; import org.simantics.scl.compiler.internal.codegen.utils.SSAValidationContext; import org.simantics.scl.compiler.top.SCLCompilerConfiguration; import gnu.trove.map.hash.THashMap; import gnu.trove.procedure.TObjectObjectProcedure; import gnu.trove.procedure.TObjectProcedure; public class SSAModule { THashMap functions = new THashMap(); ArrayList staticFields = new ArrayList(); public ArrayList closuresToGenerate = new ArrayList(); public void put(Name name, SCLConstant function) { functions.put(name, function); } public SCLConstant get(Name name) { return functions.get(name); } @Override public String toString() { final StringBuilder b = new StringBuilder(); functions.forEachEntry(new TObjectObjectProcedure() { @Override public boolean execute(Name name, SCLConstant function) { b.append(name.name); b.append(" :: "); b.append(function.getType()); b.append('\n'); if(function.isPrivate()) b.append("PRIVATE "); b.append(name); b.append(" =\n"); PrintingContext context = new PrintingContext(); context.indent(); function.getDefinition().toString(context); b.append(context.toString()); return true; } }); return b.toString(); } public void validate(SSAValidationContext context) { for(SCLConstant function : functions.values()) { try { function.getDefinition().validate(context); } catch(RuntimeException e) { System.out.println("-- VALIDATE " + function.getName() + " ----------------"); PrintingContext printingContext = new PrintingContext(); printingContext.setErrorMarker(context.errorMarker); function.getDefinition().toString(printingContext); System.out.println(printingContext.toString()); throw e; } } } public void validate() { SSAValidationContext context = new SSAValidationContext(); validate(context); //context.checkReferences(); } public boolean simplify(Environment environment, int phase) { SSASimplificationContext context = new SSASimplificationContext(this, environment, phase); for(SCLConstant function : functions.values().toArray(new SCLConstant[functions.size()])) if(functions.containsKey(function.getName())) { if(function.isPrivate() && function.hasNoOccurences()) { function.getDefinition().destroy(); functions.remove(function.getName()); context.markModified("SSAModule.dead-function " + function.getName()); } else function.simplify(context); } return context.didModify(); } public void generateCode(final ModuleBuilder moduleBuilder) throws CodeBuildingException { final String moduleClassName = moduleBuilder.getNamingPolicy().getModuleClassName(); if(SCLCompilerConfiguration.TRACE_METHOD_CREATION) System.out.println("Create class " + moduleClassName); final ClassBuilder classFile = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, moduleClassName, "java/lang/Object"); classFile.setSourceFile("_SCL_Module"); functions.forEachValue(new TObjectProcedure() { @Override public boolean execute(SCLConstant function) { if(function.getBase() == null) { Name name = function.getName(); SSAFunction definition = function.getDefinition(); String javaName = NameMangling.mangle(name.name); if(definition.getArity() == 0) { // Not a function TypeDesc typeDesc = moduleBuilder.getJavaTypeTranslator().toTypeDesc(definition.getReturnType()); if(typeDesc != TypeDesc.VOID) { // Case where typeDesc is VOID is somewhat degenerated String initClassName = moduleBuilder.getNamingPolicy().getFreshClosureClassName(); function.setBase(new JavaStaticField(initClassName, "VALUE", definition.getReturnType(), -1)); ClassBuilder initClass = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, initClassName, "java/lang/Object"); initClass.addField(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC, "VALUE", typeDesc); MethodBuilderBase classInitializer = initClass.addInitializerBase(); classInitializer.invokeStatic(moduleClassName, javaName, typeDesc, Constants.EMPTY_TYPEDESC_ARRAY); classInitializer.storeStaticField(initClassName, "VALUE", typeDesc); classInitializer.returnVoid(); classInitializer.finish(); moduleBuilder.addClass(initClass); return true; } } function.setBase(new JavaStaticMethod( moduleClassName, javaName, definition.getEffect(), definition.getTypeParameters(), definition.getReturnType(), definition.getParameterTypes())); } return true; } }); functions.forEachValue(new TObjectProcedure() { @Override public boolean execute(SCLConstant function) { JavaTypeTranslator javaTypeTranslator = moduleBuilder.getJavaTypeTranslator(); SSAFunction def = function.getDefinition(); MethodBuilder mb = classFile.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, NameMangling.mangle(function.getName().name), javaTypeTranslator.getTypeDesc(def.getReturnCont()), JavaTypeTranslator.filterVoid(javaTypeTranslator.getTypeDescs(def.getParameters())) ); def.generateCode(mb); mb.finish(); return true; } }); JavaTypeTranslator javaTypeTranslator = moduleBuilder.getJavaTypeTranslator(); for(StaticField tuple : staticFields) { classFile.addField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, tuple.name, javaTypeTranslator.toTypeDesc(tuple.type)); } classFile.addDefaultConstructor(); moduleBuilder.addClass(classFile); for(SSAClosure closure : closuresToGenerate) closure.generateCode(moduleBuilder); } /** * Marks those BoundVars that are computed when pushed * to stack and not where they are defined. */ public void markGenerateOnFly() { functions.forEachValue(new TObjectProcedure() { @Override public boolean execute(SCLConstant func) { func.getDefinition().markGenerateOnFly(); return true; } }); } public void addStaticField(StaticField tuple) { staticFields.add(tuple); } public SCLConstant remove(Name name) { return functions.remove(name); } /** * Converts all nested functions to top level functions. */ public void lambdaLift(ErrorLog errorLog) { SSALambdaLiftingContext context = new SSALambdaLiftingContext(this, errorLog); //context.validate(); for(SCLConstant function : functions.values().toArray(new SCLConstant[functions.size()])) { context.setParentName(function.getName()); function.getDefinition().lambdaLift(context); } } public void saveInlinableDefinitions() { for(SCLConstant function : functions.values()) function.saveInlinableDefinition(); } }