1 package org.simantics.scl.compiler.compilation;
3 import java.util.ArrayList;
4 import java.util.Collection;
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;
57 import gnu.trove.procedure.TObjectObjectProcedure;
58 import gnu.trove.procedure.TObjectProcedure;
60 public class CodeGeneration {
62 public static final int OPTIMIZATION_PHASES = 2;
64 CompilationContext compilationContext;
66 JavaReferenceValidator<Object, Object, Object, Object> validator;
67 ConcreteModule module;
68 ModuleBuilder moduleBuilder;
72 ExternalConstant[] externalConstants;
73 Map<String, byte[]> classes;
75 @SuppressWarnings("unchecked")
76 public CodeGeneration(CompilationContext compilationContext,
77 JavaReferenceValidator<?, ?, ?, ?> javaReferenceValidator,
78 ConcreteModule module) {
79 this.compilationContext = compilationContext;
80 this.errorLog = compilationContext.errorLog;
82 this.validator = (JavaReferenceValidator<Object, Object, Object, Object>) javaReferenceValidator;
84 moduleBuilder = new ModuleBuilder(compilationContext.namingPolicy, compilationContext.javaTypeTranslator);
87 public void simplifyValues() {
88 //System.out.println("===== Simplify values =====");
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()]);
95 for(SCLValue value : valueArray) {
96 if(value.getMacroRule() instanceof StandardMacroRule) {
97 StandardMacroRule rule = (StandardMacroRule)value.getMacroRule();
98 rule.setBaseExpression(value.getExpression().copy());
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());
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)
118 Name name = value.getName();
120 SCLConstant constant = new SCLConstant(name, value.getType());
121 value.setValue(constant);
122 /*constant.setBase(new JavaStaticMethod(
123 namingPolicy.getModuleClassName(), namingPolicy.getMethodName(name.name),
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);
134 else if(prop == PrivateProperty.INSTANCE)
135 constant.setPrivate(!isDerived);
136 else if(prop == DerivedProperty.INSTANCE) {
137 constant.setPrivate(false);
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))
148 EConstant constant = (EConstant)expression;
149 if(constant.getTypeParameters().length > 0)
152 //System.out.println(value.getName() + " <- " + constant.getValue().getName());
153 value.setValue(constant.getValue().getValue());
154 value.setExpression(null); // HMM??
156 for(SCLValue value : module.getValues()) {
158 Expression expression = value.getExpression();
159 if(expression == null)
162 DecomposedExpression decomposed =
163 DecomposedExpression.decompose(errorLog, expression);
165 CodeWriter w = mw.createFunction((SCLConstant)value.getValue(),
166 decomposed.typeParameters,
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, 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);
184 ssaModule = mw.getModule();
185 if(SCLCompilerConfiguration.DEBUG)
186 ssaModule.validate();
188 this.externalConstants = mw.getExternalConstants();
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);
196 if(SCLCompilerConfiguration.DEBUG)
197 ssaModule.validate();
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);
206 ssaModule.saveInlinableDefinitions();
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);
212 ssaModule.lambdaLift(errorLog);
213 //ssaModule.validate();
214 // TODO prevent creating more lambdas here
215 //ssaModule.simplify(environment);
216 ssaModule.markGenerateOnFly();
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);
225 ssaModule.generateCode(moduleBuilder);
226 } catch (CodeBuildingException e) {
227 errorLog.log(e.getMessage());
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();
234 public void generateDataTypes(ArrayList<StandardTypeConstructor> dataTypes) {
235 for(StandardTypeConstructor dataType : dataTypes) {
236 if(dataType.external)
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),
250 moduleBuilder.addClass(cf);
254 String javaName = MethodBuilderBase.getClassName(dataType.getTypeDesc());
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);
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),
277 moduleBuilder.addClass(cf);
283 public void generateTypeClasses() {
284 for(TypeClass typeClass : module.getTypeClasses()) {
285 final JavaTypeTranslator javaTypeTranslator = moduleBuilder.getJavaTypeTranslator();
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");
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());
298 cf.addAbstractMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, "super" + i,
299 javaTypeTranslator.toTypeDesc(sup),
300 Constants.EMPTY_TYPEDESC_ARRAY);
303 typeClass.methods.forEachValue(new TObjectProcedure<TypeClassMethod>() {
305 public boolean execute(TypeClassMethod method) {
308 mfun = Types.matchFunction(method.getBaseType(), method.getArity());
309 } catch (MatchException e) {
310 throw new InternalCompilerError("Method " + method.getName() + " has too high arity.");
312 cf.addAbstractMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, method.getJavaName(),
313 javaTypeTranslator.toTypeDesc(mfun.returnType),
314 JavaTypeTranslator.filterVoid(javaTypeTranslator.toTypeDescs(mfun.parameterTypes)));
319 moduleBuilder.addClass(cf);
323 public void generateTypeClassInstances() {
324 module.getTypeInstances().forEachEntry(new TObjectObjectProcedure<TCon, ArrayList<TypeClassInstance>>() {
327 public boolean execute(TCon typeClass, ArrayList<TypeClassInstance> instances) {
328 for(TypeClassInstance instance : instances)
329 generateTypeClassInstance(instance);
335 private void generateTypeClassInstance(final TypeClassInstance instance) {
336 final JavaTypeTranslator javaTypeTranslator = moduleBuilder.getJavaTypeTranslator();
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");
344 CodeBuilderUtils.makeRecord(cb, instance.javaName, Opcodes.ACC_PRIVATE, "cx",
345 javaTypeTranslator.toTypeDescs(instance.context), false);
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,
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);
360 instance.typeClass.methods.forEachValue(new TObjectProcedure<TypeClassMethod>() {
362 public boolean execute(TypeClassMethod method) {
364 Type baseType = method.getBaseType();
366 mfun = Types.matchFunction(baseType, method.getArity());
367 } catch (MatchException e) {
368 throw new InternalCompilerError("Method " + method.getName() + " has too high arity.");
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(),
375 JavaTypeTranslator.filterVoid(parameterTypeDescs));
377 MethodImplementation implementation =
378 instance.methodImplementations.get(method.getName());
379 if(implementation.isDefault) {
380 IVal function = compilationContext.environment.getValue(implementation.name).getValue();
382 Val[] parameters = new Val[method.getArity() + 1];
385 mfun2 = Types.matchFunction(Types.removeForAll(function.getType()), parameters.length);
386 } catch (MatchException e) {
387 throw new InternalCompilerError(e);
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]);
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)
398 mb.returnValue(returnTypeDesc);
401 IVal function = module.getValue(implementation.name.name).getValue();
403 Val[] parameters = new Val[method.getArity() + instance.context.length];
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);
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]);
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)
421 mb.returnValue(returnTypeDesc);
428 moduleBuilder.addClass(cb);