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;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
59 import gnu.trove.procedure.TObjectObjectProcedure;
60 import gnu.trove.procedure.TObjectProcedure;
62 public class CodeGeneration {
64 private static final Logger LOGGER = LoggerFactory.getLogger(CodeGeneration.class);
66 public static final int OPTIMIZATION_PHASES = 2;
68 CompilationContext compilationContext;
70 JavaReferenceValidator<Object, Object, Object, Object> validator;
71 ConcreteModule module;
72 ModuleBuilder moduleBuilder;
76 ExternalConstant[] externalConstants;
77 Map<String, byte[]> classes;
79 @SuppressWarnings("unchecked")
80 public CodeGeneration(CompilationContext compilationContext,
81 JavaReferenceValidator<?, ?, ?, ?> javaReferenceValidator,
82 ConcreteModule module) {
83 this.compilationContext = compilationContext;
84 this.errorLog = compilationContext.errorLog;
86 this.validator = (JavaReferenceValidator<Object, Object, Object, Object>) javaReferenceValidator;
88 moduleBuilder = new ModuleBuilder(compilationContext.namingPolicy, compilationContext.javaTypeTranslator);
91 public void simplifyValues() {
92 //LOGGER.info("===== Simplify values =====");
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()]);
99 for(SCLValue value : valueArray) {
100 if(value.getMacroRule() instanceof StandardMacroRule) {
101 StandardMacroRule rule = (StandardMacroRule)value.getMacroRule();
102 rule.setBaseExpression(value.getExpression().copy());
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());
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)
122 Name name = value.getName();
124 SCLConstant constant = new SCLConstant(name, value.getType());
125 value.setValue(constant);
126 /*constant.setBase(new JavaStaticMethod(
127 namingPolicy.getModuleClassName(), namingPolicy.getMethodName(name.name),
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);
138 else if(prop == PrivateProperty.INSTANCE)
139 constant.setPrivate(!isDerived);
140 else if(prop == DerivedProperty.INSTANCE) {
141 constant.setPrivate(false);
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))
152 EConstant constant = (EConstant)expression;
153 if(constant.getTypeParameters().length > 0)
156 //LOGGER.info(value.getName() + " <- " + constant.getValue().getName());
157 value.setValue(constant.getValue().getValue());
158 value.setExpression(null); // HMM??
160 for(SCLValue value : module.getValues()) {
162 Expression expression = value.getExpression();
163 if(expression == null)
166 DecomposedExpression decomposed =
167 DecomposedExpression.decompose(errorLog, expression);
169 CodeWriter w = mw.createFunction((SCLConstant)value.getValue(),
170 decomposed.typeParameters,
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);
188 ssaModule = mw.getModule();
189 if(SCLCompilerConfiguration.DEBUG)
190 ssaModule.validate();
192 this.externalConstants = mw.getExternalConstants();
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);
200 if(SCLCompilerConfiguration.DEBUG)
201 ssaModule.validate();
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);
210 ssaModule.saveInlinableDefinitions();
212 if(SCLCompilerConfiguration.SHOW_SSA_BEFORE_LAMBDA_LIFTING && SCLCompilerConfiguration.debugFilter(module.getName())) {
213 LOGGER.info("=== SSA before lambda lifting ==================================");
214 LOGGER.info("{}", ssaModule);
216 ssaModule.lambdaLift(errorLog);
217 //ssaModule.validate();
218 // TODO prevent creating more lambdas here
219 //ssaModule.simplify(environment);
220 ssaModule.markGenerateOnFly();
223 public void generateCode() {
224 if(SCLCompilerConfiguration.SHOW_FINAL_SSA && SCLCompilerConfiguration.debugFilter(module.getName())) {
225 LOGGER.info("=== Final SSA ==================================================");
226 LOGGER.info("{}", ssaModule);
229 ssaModule.generateCode(moduleBuilder);
230 } catch (CodeBuildingException e) {
231 errorLog.log(e.getMessage());
233 if(SCLCompilerConfiguration.TRACE_MAX_METHOD_SIZE && moduleBuilder.getMethodSizeCounter() != null)
234 LOGGER.info("[Max method size] " + module.getName() + ": " + moduleBuilder.getMethodSizeCounter());
235 classes = moduleBuilder.getClasses();
238 public void generateDataTypes(ArrayList<StandardTypeConstructor> dataTypes) {
239 for(StandardTypeConstructor dataType : dataTypes) {
240 if(dataType.external)
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),
254 moduleBuilder.addClass(cf);
258 String javaName = MethodBuilderBase.getClassName(dataType.getTypeDesc());
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);
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),
281 moduleBuilder.addClass(cf);
287 public void generateTypeClasses() {
288 for(TypeClass typeClass : module.getTypeClasses()) {
289 final JavaTypeTranslator javaTypeTranslator = moduleBuilder.getJavaTypeTranslator();
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");
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());
302 cf.addAbstractMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, "super" + i,
303 javaTypeTranslator.toTypeDesc(sup),
304 Constants.EMPTY_TYPEDESC_ARRAY);
307 typeClass.methods.forEachValue(new TObjectProcedure<TypeClassMethod>() {
309 public boolean execute(TypeClassMethod method) {
312 mfun = Types.matchFunction(method.getBaseType(), method.getArity());
313 } catch (MatchException e) {
314 throw new InternalCompilerError("Method " + method.getName() + " has too high arity.");
316 cf.addAbstractMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, method.getJavaName(),
317 javaTypeTranslator.toTypeDesc(mfun.returnType),
318 JavaTypeTranslator.filterVoid(javaTypeTranslator.toTypeDescs(mfun.parameterTypes)));
323 moduleBuilder.addClass(cf);
327 public void generateTypeClassInstances() {
328 module.getTypeInstances().forEachEntry(new TObjectObjectProcedure<TCon, ArrayList<TypeClassInstance>>() {
331 public boolean execute(TCon typeClass, ArrayList<TypeClassInstance> instances) {
332 for(TypeClassInstance instance : instances)
333 generateTypeClassInstance(instance);
339 private void generateTypeClassInstance(final TypeClassInstance instance) {
340 final JavaTypeTranslator javaTypeTranslator = moduleBuilder.getJavaTypeTranslator();
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");
348 CodeBuilderUtils.makeRecord(cb, instance.javaName, Opcodes.ACC_PRIVATE, "cx",
349 javaTypeTranslator.toTypeDescs(instance.context), false);
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,
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);
364 instance.typeClass.methods.forEachValue(new TObjectProcedure<TypeClassMethod>() {
366 public boolean execute(TypeClassMethod method) {
368 Type baseType = method.getBaseType();
370 mfun = Types.matchFunction(baseType, method.getArity());
371 } catch (MatchException e) {
372 throw new InternalCompilerError("Method " + method.getName() + " has too high arity.");
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(),
379 JavaTypeTranslator.filterVoid(parameterTypeDescs));
381 MethodImplementation implementation =
382 instance.methodImplementations.get(method.getName());
383 if(implementation.isDefault) {
384 IVal function = compilationContext.environment.getValue(implementation.name).getValue();
386 Val[] parameters = new Val[method.getArity() + 1];
389 mfun2 = Types.matchFunction(Types.removeForAll(function.getType()), parameters.length);
390 } catch (MatchException e) {
391 throw new InternalCompilerError(e);
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]);
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)
402 mb.returnValue(returnTypeDesc);
405 IVal function = module.getValue(implementation.name.name).getValue();
407 Val[] parameters = new Val[method.getArity() + instance.context.length];
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);
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]);
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)
425 mb.returnValue(returnTypeDesc);
432 moduleBuilder.addClass(cb);