]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/CodeGeneration.java
(refs #7250) CHR rules modularization (first working version)
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / compilation / CodeGeneration.java
1 package org.simantics.scl.compiler.compilation;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Map;
6
7 import org.cojen.classfile.TypeDesc;
8 import org.objectweb.asm.Opcodes;
9 import org.simantics.scl.compiler.common.datatypes.Constructor;
10 import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
11 import org.simantics.scl.compiler.common.names.Name;
12 import org.simantics.scl.compiler.constants.LocalFieldConstant;
13 import org.simantics.scl.compiler.constants.LocalVariableConstant;
14 import org.simantics.scl.compiler.constants.NoRepConstant;
15 import org.simantics.scl.compiler.constants.SCLConstant;
16 import org.simantics.scl.compiler.constants.ThisConstant;
17 import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext;
18 import org.simantics.scl.compiler.elaboration.expressions.Expression;
19 import org.simantics.scl.compiler.elaboration.macros.StandardMacroRule;
20 import org.simantics.scl.compiler.elaboration.modules.InlineProperty;
21 import org.simantics.scl.compiler.elaboration.modules.MethodImplementation;
22 import org.simantics.scl.compiler.elaboration.modules.PrivateProperty;
23 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
24 import org.simantics.scl.compiler.elaboration.modules.SCLValueProperty;
25 import org.simantics.scl.compiler.elaboration.modules.TypeClass;
26 import org.simantics.scl.compiler.elaboration.modules.TypeClassInstance;
27 import org.simantics.scl.compiler.elaboration.modules.TypeClassMethod;
28 import org.simantics.scl.compiler.errors.ErrorLog;
29 import org.simantics.scl.compiler.errors.Locations;
30 import org.simantics.scl.compiler.internal.codegen.references.IVal;
31 import org.simantics.scl.compiler.internal.codegen.references.Val;
32 import org.simantics.scl.compiler.internal.codegen.ssa.SSAModule;
33 import org.simantics.scl.compiler.internal.codegen.types.JavaReferenceValidator;
34 import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator;
35 import org.simantics.scl.compiler.internal.codegen.types.StandardTypeConstructor;
36 import org.simantics.scl.compiler.internal.codegen.utils.ClassBuilder;
37 import org.simantics.scl.compiler.internal.codegen.utils.CodeBuilderUtils;
38 import org.simantics.scl.compiler.internal.codegen.utils.CodeBuildingException;
39 import org.simantics.scl.compiler.internal.codegen.utils.Constants;
40 import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;
41 import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilderBase;
42 import org.simantics.scl.compiler.internal.codegen.utils.ModuleBuilder;
43 import org.simantics.scl.compiler.internal.codegen.writer.CodeWriter;
44 import org.simantics.scl.compiler.internal.codegen.writer.ExternalConstant;
45 import org.simantics.scl.compiler.internal.codegen.writer.ModuleWriter;
46 import org.simantics.scl.compiler.internal.elaboration.decomposed.DecomposedExpression;
47 import org.simantics.scl.compiler.module.ConcreteModule;
48 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
49 import org.simantics.scl.compiler.types.TCon;
50 import org.simantics.scl.compiler.types.TPred;
51 import org.simantics.scl.compiler.types.Type;
52 import org.simantics.scl.compiler.types.Types;
53 import org.simantics.scl.compiler.types.exceptions.MatchException;
54 import org.simantics.scl.compiler.types.util.MultiFunction;
55
56 import gnu.trove.procedure.TObjectObjectProcedure;
57 import gnu.trove.procedure.TObjectProcedure;
58
59 public class CodeGeneration {
60     
61     public static final int OPTIMIZATION_PHASES = 2;
62     
63     CompilationContext compilationContext;
64     ErrorLog errorLog;
65     JavaReferenceValidator<Object, Object, Object, Object> validator;
66     ConcreteModule module;
67     ModuleBuilder moduleBuilder;
68     
69     // creates
70     SSAModule ssaModule;
71     ExternalConstant[] externalConstants;
72     Map<String, byte[]> classes;
73     
74     @SuppressWarnings("unchecked")
75     public CodeGeneration(CompilationContext compilationContext,
76             JavaReferenceValidator<?, ?, ?, ?> javaReferenceValidator,
77             ConcreteModule module) {    
78         this.compilationContext = compilationContext;
79         this.errorLog = compilationContext.errorLog;
80         this.module = module;
81         this.validator = (JavaReferenceValidator<Object, Object, Object, Object>) javaReferenceValidator;
82         
83         moduleBuilder = new ModuleBuilder(compilationContext.namingPolicy, compilationContext.javaTypeTranslator);
84     }
85     
86     public void simplifyValues() {
87         //System.out.println("===== Simplify values =====");
88         
89         Collection<SCLValue> values = module.getValues();
90         SimplificationContext simplificationContext = new SimplificationContext(compilationContext, validator);
91         //System.out.println("-----------------------------------------------");
92         SCLValue[] valueArray = values.toArray(new SCLValue[values.size()]);
93         
94         for(SCLValue value : valueArray) {
95             if(value.getMacroRule() instanceof StandardMacroRule) {
96                 StandardMacroRule rule = (StandardMacroRule)value.getMacroRule();
97                 rule.setBaseExpression(value.getExpression().copy());
98             }
99         }
100         
101         // Simplify
102         for(SCLValue value : valueArray) {
103             //System.out.println("BEFORE " + value.getName() + " = " + value.getExpression());
104             value.getSimplifiedExpression(simplificationContext);
105             //System.out.println("AFTER " + value.getName() + " = " + value.getExpression());
106         }
107     }
108     
109     public void convertToSSA() {
110         ModuleWriter mw = new ModuleWriter(compilationContext.namingPolicy.getModuleClassName());
111         for(SCLValue value : module.getValues()) {
112             //System.out.println(value.getName().name + " :: " + value.getType());
113             Expression expression = value.getExpression();
114             if(expression == null)
115                 continue;
116
117             Name name = value.getName();
118             
119             SCLConstant constant = new SCLConstant(name, value.getType());
120             value.setValue(constant);            
121             /*constant.setBase(new JavaStaticMethod(
122                     namingPolicy.getModuleClassName(), namingPolicy.getMethodName(name.name), 
123                     decomposed.effect,
124                     decomposed.typeParameters, 
125                     decomposed.returnType, 
126                     decomposed.parameterTypes));*/
127             for(SCLValueProperty prop : value.getProperties()) {
128                 if(prop instanceof InlineProperty) {
129                     InlineProperty inlineProperty = (InlineProperty)prop;
130                     constant.setInlineArity(inlineProperty.arity, inlineProperty.phaseMask);
131                 }
132                 else if(prop == PrivateProperty.INSTANCE)
133                     constant.setPrivate(true);
134             }
135         }
136         // This is quite hackish optimization that can be possibly removed when
137         // better optimizations exist
138         /*for(SCLValue value : module.getValues()) {
139             Expression expression = value.getExpression();
140             if(!(expression instanceof EConstant))
141                 continue;
142             EConstant constant = (EConstant)expression;
143             if(constant.getTypeParameters().length > 0)
144                 continue;
145             
146             //System.out.println(value.getName() + " <- " + constant.getValue().getName());
147             value.setValue(constant.getValue().getValue());
148             value.setExpression(null); // HMM??
149         }*/
150         for(SCLValue value : module.getValues()) {
151             try {
152                 Expression expression = value.getExpression();
153                 if(expression == null)
154                     continue;
155      
156                 DecomposedExpression decomposed = 
157                         DecomposedExpression.decompose(expression);
158     
159                 CodeWriter w = mw.createFunction((SCLConstant)value.getValue(),
160                         decomposed.typeParameters,
161                         decomposed.effect,
162                         decomposed.returnType, 
163                         decomposed.parameterTypes);    
164                 if(value.getValue() instanceof SCLConstant) // FIXME should be redundant test, if expression is nulled above
165                     ((SCLConstant)value.getValue()).setDefinition(w.getFunction());
166                 IVal[] parameterVals = w.getParameters();
167                 for(int i=0;i<decomposed.parameters.length;++i)
168                     decomposed.parameters[i].setVal(parameterVals[i]);
169                 w.return_(decomposed.body.toVal(compilationContext.environment, w));            
170             } catch(RuntimeException e) {
171                 long location = value.getExpression().location;
172                 if(location == Locations.NO_LOCATION)
173                     location = value.definitionLocation;
174                 errorLog.setExceptionPosition(location);
175                 throw e;
176             }
177         }
178         ssaModule = mw.getModule();
179         if(SCLCompilerConfiguration.DEBUG)
180             ssaModule.validate();
181         
182         this.externalConstants = mw.getExternalConstants();
183     }
184     
185     public void optimizeSSA() {
186         if(SCLCompilerConfiguration.SHOW_SSA_BEFORE_OPTIMIZATION && SCLCompilerConfiguration.debugFilter(module.getName())) {
187             System.out.println("=== SSA before optimization ====================================");
188             System.out.println(ssaModule);            
189         }
190         if(SCLCompilerConfiguration.DEBUG)
191         ssaModule.validate();
192         int optCount = 0;
193         for(int phase=0;phase<OPTIMIZATION_PHASES;++phase) {
194             while(optCount++ < 100 && ssaModule.simplify(compilationContext.environment, phase)) {
195                 //System.out.println("simplify " + optCount);
196                 //System.out.println("================================================================");
197                 //System.out.println(ssaModule);        
198             }
199             if(phase == 0)
200                 ssaModule.saveInlinableDefinitions();
201         }
202         if(SCLCompilerConfiguration.SHOW_SSA_BEFORE_LAMBDA_LIFTING && SCLCompilerConfiguration.debugFilter(module.getName())) {
203             System.out.println("=== SSA before lambda lifting ==================================");
204             System.out.println(ssaModule);            
205         }
206         ssaModule.lambdaLift(errorLog);
207         //ssaModule.validate();
208         // TODO prevent creating more lambdas here
209         //ssaModule.simplify(environment);
210         ssaModule.markGenerateOnFly();
211     }
212     
213     public void generateCode() {
214         if(SCLCompilerConfiguration.SHOW_FINAL_SSA && SCLCompilerConfiguration.debugFilter(module.getName())) {
215             System.out.println("=== Final SSA ==================================================");
216             System.out.println(ssaModule);
217         }
218         try {
219             ssaModule.generateCode(moduleBuilder);
220         } catch (CodeBuildingException e) {
221             errorLog.log(e.getMessage());
222         }
223         if(SCLCompilerConfiguration.TRACE_MAX_METHOD_SIZE && moduleBuilder.getMethodSizeCounter() != null)
224             System.out.println("[Max method size] " + module.getName() + ": " + moduleBuilder.getMethodSizeCounter());
225         classes = moduleBuilder.getClasses();
226     }
227     
228     public void generateDataTypes(ArrayList<StandardTypeConstructor> dataTypes) {
229         for(StandardTypeConstructor dataType : dataTypes) {
230             if(dataType.external)
231                 continue;
232             if(dataType.constructors.length == 1) {
233                 Constructor constructor = dataType.constructors[0];
234                 if(constructor.parameterTypes.length != 1) {                    
235                     String javaName = MethodBuilderBase.getClassName(dataType.getTypeDesc());
236                     if(SCLCompilerConfiguration.TRACE_METHOD_CREATION)
237                         System.out.println("Create class " + javaName);
238                     ClassBuilder cf = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, javaName, "java/lang/Object");
239                     cf.setSourceFile("_SCL_DataType");
240                     CodeBuilderUtils.makeRecord(cf, constructor.name.name,
241                             Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "c", 
242                             compilationContext.javaTypeTranslator.toTypeDescs(constructor.parameterTypes),
243                             true);
244                     moduleBuilder.addClass(cf);
245                 }
246             }
247             else {                
248                 String javaName = MethodBuilderBase.getClassName(dataType.getTypeDesc());
249                 // Create supertype
250                 {
251                     if(SCLCompilerConfiguration.TRACE_METHOD_CREATION)
252                         System.out.println("Create class " + javaName);
253                     ClassBuilder cf = new ClassBuilder(moduleBuilder,
254                             Opcodes.ACC_ABSTRACT | Opcodes.ACC_PUBLIC,
255                             javaName, "java/lang/Object");
256                     cf.setSourceFile("_SCL_DataType");
257                     cf.addDefaultConstructor();
258                     moduleBuilder.addClass(cf);
259                 }
260     
261                 // Create constructors
262                 for(Constructor constructor : dataType.constructors) {
263                     if(SCLCompilerConfiguration.TRACE_METHOD_CREATION)
264                         System.out.println("Create class " + constructor.javaName);
265                     ClassBuilder cf = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, constructor.javaName, javaName);
266                     cf.setSourceFile("_SCL_DataType");
267                     CodeBuilderUtils.makeRecord(cf, constructor.name.name,
268                             Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "c", 
269                             compilationContext.javaTypeTranslator.toTypeDescs(constructor.parameterTypes),
270                             true);
271                     moduleBuilder.addClass(cf);
272                 }
273             }
274         }
275     }
276     
277     public void generateTypeClasses() {
278         for(TypeClass typeClass : module.getTypeClasses()) {
279             final JavaTypeTranslator javaTypeTranslator = moduleBuilder.getJavaTypeTranslator();
280             
281             if(SCLCompilerConfiguration.TRACE_METHOD_CREATION)
282                 System.out.println("Create class " + typeClass.javaName);
283             final ClassBuilder cf = new ClassBuilder(moduleBuilder,
284                     Opcodes.ACC_INTERFACE | Opcodes.ACC_PUBLIC,
285                     typeClass.javaName, "java/lang/Object");
286   
287             for(int i=0;i<typeClass.context.length;++i) {
288                 TPred sup = typeClass.context[i];
289                 /*if(Types.equals(sup.parameters, typeClass.parameters))
290                     cf.addInterface(javaTypeTranslator.toTypeDesc(sup).getDescriptor());
291                 else*/
292                     cf.addAbstractMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, "super" + i,
293                             javaTypeTranslator.toTypeDesc(sup),
294                             Constants.EMPTY_TYPEDESC_ARRAY);
295             }
296             
297             typeClass.methods.forEachValue(new TObjectProcedure<TypeClassMethod>() {            
298                 @Override
299                 public boolean execute(TypeClassMethod method) {
300                     MultiFunction mfun;
301                     try {
302                         mfun = Types.matchFunction(method.getBaseType(), method.getArity());
303                     } catch (MatchException e) {
304                         throw new InternalCompilerError("Method " + method.getName() + " has too high arity.");
305                     }
306                     cf.addAbstractMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, method.getJavaName(), 
307                             javaTypeTranslator.toTypeDesc(mfun.returnType),
308                             JavaTypeTranslator.filterVoid(javaTypeTranslator.toTypeDescs(mfun.parameterTypes)));
309                     return true;
310                 }
311             });
312     
313             moduleBuilder.addClass(cf);
314         }
315     }
316     
317     public void generateTypeClassInstances() {
318         module.getTypeInstances().forEachEntry(new TObjectObjectProcedure<TCon, ArrayList<TypeClassInstance>>() {
319             
320             @Override
321             public boolean execute(TCon typeClass, ArrayList<TypeClassInstance> instances) {
322                 for(TypeClassInstance instance : instances)
323                     generateTypeClassInstance(instance);
324                 return true;
325             }
326         });
327     }
328
329     private void generateTypeClassInstance(final TypeClassInstance instance) {
330         final JavaTypeTranslator javaTypeTranslator = moduleBuilder.getJavaTypeTranslator();
331         
332         if(SCLCompilerConfiguration.TRACE_METHOD_CREATION)
333             System.out.println("Create class " + instance.javaName);
334         final ClassBuilder cb = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, instance.javaName, "java/lang/Object",
335                 instance.typeClass.javaName);
336         cb.setSourceFile("_SCL_TypeClassInstance");
337         
338         CodeBuilderUtils.makeRecord(cb, instance.javaName, Opcodes.ACC_PRIVATE, "cx", 
339                 javaTypeTranslator.toTypeDescs(instance.context), false);
340
341         for(int i=0;i<instance.superExpressions.length;++i) {
342             TypeDesc returnTypeDesc = javaTypeTranslator.toTypeDesc(instance.typeClass.context[i]); 
343             MethodBuilder mb = cb.addMethod(Opcodes.ACC_PUBLIC, "super" + i,
344                     returnTypeDesc,
345                     Constants.EMPTY_TYPEDESC_ARRAY);
346             Val[] parameters = new Val[instance.context.length];
347             for(int j=0;j<instance.context.length;++j)     
348                 parameters[j] = new LocalFieldConstant(instance.context[j], "cx"+j);
349             instance.superExpressions[i].getValue().apply(mb, Type.EMPTY_ARRAY, parameters);
350             mb.returnValue(returnTypeDesc);
351             mb.finish();
352         }
353         
354         instance.typeClass.methods.forEachValue(new TObjectProcedure<TypeClassMethod>() {            
355             @Override
356             public boolean execute(TypeClassMethod method) {
357                 MultiFunction mfun;
358                 Type baseType = method.getBaseType();
359                 try {                    
360                     mfun = Types.matchFunction(baseType, method.getArity());
361                 } catch (MatchException e) {
362                     throw new InternalCompilerError("Method " + method.getName() + " has too high arity.");
363                 }
364                 //System.out.println("Interface types: " + Arrays.toString(types));
365                 TypeDesc[] parameterTypeDescs = javaTypeTranslator.toTypeDescs(mfun.parameterTypes);
366                 TypeDesc returnTypeDesc = javaTypeTranslator.toTypeDesc(mfun.returnType); 
367                 MethodBuilder mb = cb.addMethod(Opcodes.ACC_PUBLIC, method.getJavaName(), 
368                         returnTypeDesc,
369                         JavaTypeTranslator.filterVoid(parameterTypeDescs));
370                 
371                 MethodImplementation implementation = 
372                         instance.methodImplementations.get(method.getName());
373                 if(implementation.isDefault) {
374                     IVal function = compilationContext.environment.getValue(implementation.name).getValue();
375                     
376                     Val[] parameters = new Val[method.getArity() + 1];
377                     MultiFunction mfun2;
378                     try {
379                         mfun2 = Types.matchFunction(Types.removeForAll(function.getType()), parameters.length);
380                     } catch (MatchException e) {
381                         throw new InternalCompilerError(e);
382                     }
383                     parameters[0] = new ThisConstant(instance.instance);
384                     for(int i=0,j=0;i<method.getArity();++i)
385                         if(javaTypeTranslator.toTypeDesc(mfun2.parameterTypes[1 + i]).equals(TypeDesc.VOID))
386                             parameters[1+i] = new NoRepConstant(mfun2.parameterTypes[1 + i]);
387                         else
388                             parameters[1+i] = new LocalVariableConstant(mfun2.parameterTypes[1 + i], mb.getParameter(j++));
389                     Type returnType = function.apply(mb, Type.EMPTY_ARRAY, parameters);
390                     if(returnTypeDesc == TypeDesc.OBJECT)
391                         mb.box(returnType);
392                     mb.returnValue(returnTypeDesc);
393                 }
394                 else {
395                     IVal function = module.getValue(implementation.name.name).getValue();
396                     
397                     Val[] parameters = new Val[method.getArity() + instance.context.length];
398                     MultiFunction mfun2;
399                     try {
400                         mfun2 = Types.matchFunction(Types.removeForAll(function.getType()), parameters.length);
401                         //System.out.println("Implementation types: " + Arrays.toString(functionTypes));
402                     } catch (MatchException e) {
403                         throw new InternalCompilerError(e);
404                     }
405                     for(int i=0;i<instance.context.length;++i) 
406                         parameters[i] = new LocalFieldConstant(instance.context[i], "cx"+i);
407                     for(int i=0,j=0;i<method.getArity();++i)
408                         if(javaTypeTranslator.toTypeDesc(mfun2.parameterTypes[instance.context.length + i]).equals(TypeDesc.VOID))
409                             parameters[instance.context.length+i] = new NoRepConstant(mfun2.parameterTypes[instance.context.length + i]);
410                         else
411                             parameters[instance.context.length+i] = new LocalVariableConstant(mfun2.parameterTypes[instance.context.length + i], mb.getParameter(j++));
412                     Type returnType = function.apply(mb, Type.EMPTY_ARRAY, parameters);
413                     if(returnTypeDesc == TypeDesc.OBJECT)
414                         mb.box(returnType);
415                     mb.returnValue(returnTypeDesc);
416                 }
417                 mb.finish();
418                 return true;
419             }
420         });
421
422         moduleBuilder.addClass(cb);
423     }
424 }