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