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