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