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