Mercurial > hg > truffle
diff graal/com.oracle.max.asmdis/src/com/sun/max/asm/gen/AssemblerGenerator.java @ 3733:e233f5660da4
Added Java files from Maxine project.
author | Thomas Wuerthinger <thomas.wuerthinger@oracle.com> |
---|---|
date | Sat, 17 Dec 2011 19:59:18 +0100 |
parents | |
children | bc8527f3071c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.asmdis/src/com/sun/max/asm/gen/AssemblerGenerator.java Sat Dec 17 19:59:18 2011 +0100 @@ -0,0 +1,809 @@ +/* + * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.max.asm.gen; + +import static com.sun.max.lang.Classes.*; + +import java.io.*; +import java.lang.reflect.*; +import java.util.*; + +import com.sun.max.*; +import com.sun.max.asm.*; +import com.sun.max.asm.dis.*; +import com.sun.max.ide.*; +import com.sun.max.io.*; +import com.sun.max.lang.*; +import com.sun.max.program.*; +import com.sun.max.program.option.*; + +/** + * Source code generator for raw and label assembler methods derived from an ISA specification. + */ +public abstract class AssemblerGenerator<Template_Type extends Template> { + + protected OptionSet options = new OptionSet(); + + public final Option<File> outputDirectoryOption = options.newFileOption("d", JavaProject.findSourceDirectory(AssemblerGenerator.class), + "Source directory of the class(es) containing the for generated assembler methods."); + public final Option<String> assemblerInterfaceNameOption = options.newStringOption("i", null, + "Interface used to constrain which assembler methods will be generated. " + + "If absent, an assembler method is generated for each template in the specification."); + public final Option<String> rawAssemblerClassNameOption = options.newStringOption("r", null, + "Class containing the generated raw assembler methods."); + public final Option<String> labelAssemblerClassNameOption = options.newStringOption("l", null, + "Class containing the generated label assembler methods."); + public final Option<Boolean> generateRedundantInstructionsOption = options.newBooleanOption("redundant", true, + "Generate assembler methods for redundant templates. Two templates are redundant if they " + + "both have the same name and operands. Redundant pairs of instructions are assumed to " + + "implement the same machine instruction semantics but may have different encodings."); + + private final Assembly<Template_Type> assembly; + private final boolean sortAssemblerMethods; + private List<Template_Type> templates; + private List<Template_Type> labelTemplates; + + protected AssemblerGenerator(Assembly<Template_Type> assembly, boolean sortAssemblerMethods) { + Trace.addTo(options); + this.assembly = assembly; + final String isa = assembly.isa().name(); + final String defaultOutputPackage = getPackageName(Assembler.class) + "." + isa.toLowerCase() + ".complete"; + this.rawAssemblerClassNameOption.setDefaultValue(defaultOutputPackage + "." + isa + "RawAssembler"); + this.labelAssemblerClassNameOption.setDefaultValue(defaultOutputPackage + "." + isa + "LabelAssembler"); + this.sortAssemblerMethods = sortAssemblerMethods; + } + + public Assembly<Template_Type> assembly() { + return assembly; + } + + static class MethodKey { + final String name; + final Class[] parameterTypes; + + MethodKey(Method method) { + name = method.getName(); + parameterTypes = method.getParameterTypes(); + } + + MethodKey(Template template, boolean asLabelTemplate) { + name = template.assemblerMethodName(); + parameterTypes = template.parameterTypes(); + if (asLabelTemplate) { + final int labelParameterIndex = template.labelParameterIndex(); + assert labelParameterIndex != -1; + parameterTypes[labelParameterIndex] = Label.class; + } + } + + @Override + public boolean equals(Object object) { + if (object instanceof MethodKey) { + final MethodKey other = (MethodKey) object; + return other.name.equals(name) && Arrays.equals(parameterTypes, other.parameterTypes); + } + return false; + } + + @Override + public int hashCode() { + return name.hashCode() ^ parameterTypes.length; + } + + @Override + public String toString() { + final String paramTypes = Arrays.toString(this.parameterTypes); + return name + "(" + paramTypes.substring(1, paramTypes.length() - 1) + ")"; + } + + } + + private List<Template_Type> filterTemplates(List<Template_Type> templates) { + if (!generateRedundantInstructionsOption.getValue()) { + final List<Template_Type> result = new LinkedList<Template_Type>(); + for (Template_Type template : templates) { + if (!template.isRedundant()) { + result.add(template); + } + } + return result; + } + return templates; + } + + /** + * Initializes the set of label and raw templates that will be generated as assembler methods. + * This includes doing any filtering out of templates based on an {@linkplain #assemblerInterfaceNameOption assembler interface}. + */ + private void initTemplates() { + assert (labelTemplates == null) == (templates == null); + if (templates == null) { + final String assemblerInterfaceName = assemblerInterfaceNameOption.getValue(); + if (assemblerInterfaceName == null) { + templates = filterTemplates(assembly().templates()); + labelTemplates = filterTemplates(assembly().labelTemplates()); + } else { + final List<Template_Type> newTemplates = new ArrayList<Template_Type>(); + final List<Template_Type> newLabelTemplates = new ArrayList<Template_Type>(); + + Class assemberInterface = null; + try { + assemberInterface = Class.forName(assemblerInterfaceName); + ProgramError.check(assemberInterface.isInterface(), "The class " + assemblerInterfaceName + " is not an interface"); + } catch (ClassNotFoundException e) { + throw ProgramError.unexpected("The assembler interface class " + assemblerInterfaceName + " must be on the class path"); + } + final Set<MethodKey> assemblerInterfaceMethods = new HashSet<MethodKey>(); + for (Method assemblerInterfaceMethod : assemberInterface.getDeclaredMethods()) { + assemblerInterfaceMethods.add(new MethodKey(assemblerInterfaceMethod)); + } + + for (Template_Type labelTemplate : assembly().labelTemplates()) { + if (assemblerInterfaceMethods.contains(new MethodKey(labelTemplate, true))) { + assert labelTemplate.labelParameterIndex() != -1; + newLabelTemplates.add(labelTemplate); + } + } + + for (Template_Type template : assembly().templates()) { + if (template.labelParameterIndex() != -1 && assemblerInterfaceMethods.contains(new MethodKey(template, true))) { + newTemplates.add(template); + } else if (assemblerInterfaceMethods.contains(new MethodKey(template, false))) { + newTemplates.add(template); + } + } + + this.templates = newTemplates; + this.labelTemplates = newLabelTemplates; + + Trace.line(1, "Based on " + assemberInterface + ", " + (assembly().templates().size() - newTemplates.size()) + " (of " + assembly().templates().size() + ") raw templates and " + + (assembly().labelTemplates().size() - newLabelTemplates.size()) + " (of " + assembly().labelTemplates().size() + ") label templates will be omitted from generated assembler methods"); + } + + if (sortAssemblerMethods) { + Class<Template_Type[]> type = null; + Template_Type[] sortedTemplates = Utils.cast(type, Array.newInstance(assembly().templateType(), templates.size())); + Template_Type[] sortedLabelTemplates = Utils.cast(type, Array.newInstance(assembly().templateType(), labelTemplates.size())); + templates.toArray(sortedTemplates); + Arrays.sort(sortedTemplates); + templates = Arrays.asList(sortedTemplates); + labelTemplates.toArray(sortedLabelTemplates); + Arrays.sort(sortedLabelTemplates); + labelTemplates = Arrays.asList(sortedLabelTemplates); + } + } + } + + protected final List<Template_Type> templates() { + initTemplates(); + return templates; + } + + protected final List<Template_Type> labelTemplates() { + initTemplates(); + return labelTemplates; + } + + /** + * Gets the absolute path to the source file that will updated to include the generated assembler methods. + * + * @param className the name of the Java class that contains the generated assembler methods + */ + private File getSourceFileFor(String className) { + return new File(outputDirectoryOption.getValue(), className.replace('.', File.separatorChar) + ".java").getAbsoluteFile(); + } + + protected final String formatParameterList(String separator, List<? extends Parameter> parameters, boolean typesOnly) { + String sep = separator; + final StringBuilder sb = new StringBuilder(); + for (Parameter parameter : parameters) { + sb.append(sep); + sb.append(Classes.getSimpleName(parameter.type(), true)); + if (!typesOnly) { + sb.append(" "); + sb.append(parameter.variableName()); + } + if (!sep.startsWith(", ")) { + sep = ", " + sep; + } + } + return sb.toString(); + } + + /** + * Prints the source code for the raw assembler method for to a given template. + * + * @return the number of source code lines printed + */ + protected abstract int printMethod(IndentWriter writer, Template_Type template); + + /** + * Prints the source code for support methods that are used by the methods printed by {@link #printMethod(IndentWriter, Template)}. + * + * @return the number of subroutines printed + */ + protected int printSubroutines(IndentWriter writer) { + return 0; + } + + /** + * Gets the set of packages that must be imported for the generated code to compile successfully. + * + * @param className the name of the Java class that contains the assembler methods generated from {@code templates} + * @param templateList the list of templates for which code is being generated + * @return a set of packages sorted by name + */ + public Set<String> getImportPackages(String className, Iterable<Template_Type> templateList) { + final String outputPackage = getPackageName(className); + final Set<String> packages = new TreeSet<String>(); + packages.add(getPackageName(AssemblyException.class)); + packages.add(getPackageName(Label.class)); + for (Template_Type template : templateList) { + for (Parameter parameter : template.parameters()) { + final Class type = parameter.type(); + if (!type.isPrimitive()) { + final String p = getPackageName(type); + if (!p.equals(outputPackage)) { + packages.add(p); + } + } + } + } + return packages; + } + + /** + * Prints the Javadoc comment for a template followed by a C++ style comment stating the template's number (it's + * position in the order of emitted assembler methods) and its serial (a unique identifier given to every template). + */ + protected void printMethodComment(IndentWriter writer, Template_Type template, int number, boolean forLabelAssemblerMethod) { + printMethodJavadoc(writer, template, forLabelAssemblerMethod); + writer.println("// Template#: " + number + ", Serial#: " + template.serial()); + } + + /** + * Determines if a given label template should be omitted from assembler method generation. + * This method is overridden by subclasses that may generate the code for 2 related label templates + * in a single assembler method. For example, on X86 most branch instructions can take offsets of variable bit widths + * and the logic for decoding the bit width of a {@link Label} value may be generated in a single assembler method. + * <p> + * The default implementation of this method returns {@code false}. + */ + protected boolean omitLabelTemplate(Template_Type labelTemplate) { + return false; + } + + /** + * Gets a reference to the architecture manual section describing the given template. The + * returned string should conform to the format of the {@code @see} Javadoc tag. + */ + protected String getJavadocManualReference(Template_Type template) { + return null; + } + + /** + * Allows subclasses to print ISA specific details for a template. For example, RISC synthetic instructions + * print what raw instruction they are derived from. + * + * @param extraLinks + * a sequence to which extra javadoc links should be appended + */ + protected void printExtraMethodJavadoc(IndentWriter writer, Template_Type template, List<String> extraLinks, boolean forLabelAssemblerMethod) { + } + + private boolean seenNoSuchAssemblerMethodError; + + /** + * Writes the Javadoc comment for an assembler method. + * + * @param template the template from which the assembler method is generated + */ + protected void printMethodJavadoc(IndentWriter writer, Template_Type template, boolean forLabelAssemblerMethod) { + final List<String> extraLinks = new LinkedList<String>(); + final List<? extends Parameter> parameters = getParameters(template, forLabelAssemblerMethod); + writer.println("/**"); + writer.println(" * Pseudo-external assembler syntax: {@code " + template.externalName() + externalMnemonicSuffixes(parameters) + " }" + externalParameters(parameters)); + + final boolean printExampleInstruction = true; + if (printExampleInstruction) { + + final List<Argument> arguments = new ArrayList<Argument>(); + final AddressMapper addressMapper = new AddressMapper(); + for (Parameter p : template.parameters()) { + final Argument exampleArg = p.getExampleArgument(); + if (exampleArg != null) { + arguments.add(exampleArg); + } else { + break; + } + } + if (arguments.size() == template.parameters().size()) { + try { + final DisassembledInstruction instruction = generateExampleInstruction(template, arguments); + final ImmediateArgument targetAddress = instruction.targetAddress(); + + if (targetAddress != null) { + addressMapper.add(targetAddress, "L1"); + } + final String exampleInstruction = instruction.toString(addressMapper); + writer.println(" * Example disassembly syntax: {@code " + exampleInstruction + "}"); + } catch (NoSuchAssemblerMethodError e) { + if (!seenNoSuchAssemblerMethodError) { + seenNoSuchAssemblerMethodError = true; + ProgramWarning.message("Once generated assembler has been compiled, re-generate it you want a usage example " + + "in the Javadoc for every generated assembler method"); + } + } catch (AssemblyException e) { + ProgramWarning.message("Error generating example instruction: " + e); + } + } + } + + printExtraMethodJavadoc(writer, template, extraLinks, forLabelAssemblerMethod); + final List<InstructionConstraint> constraints = new ArrayList<InstructionConstraint>(template.instructionDescription().specifications().size()); + for (Object s : template.instructionDescription().specifications()) { + if (s instanceof InstructionConstraint) { + constraints.add((InstructionConstraint) s); + } + } + if (!constraints.isEmpty()) { + writer.println(" * <p>"); + for (InstructionConstraint constraint : constraints) { + final Method predicateMethod = constraint.predicateMethod(); + if (predicateMethod != null) { + extraLinks.add(predicateMethod.getDeclaringClass().getName() + "#" + predicateMethod.getName()); + } + writer.println(" * Constraint: {@code " + constraint.asJavaExpression() + "}<br />"); + } + } + + if (!extraLinks.isEmpty()) { + writer.println(" *"); + for (String link : extraLinks) { + writer.println(" * @see " + link); + } + } + + final String ref = getJavadocManualReference(template); + if (ref != null) { + writer.println(" *"); + writer.println(" * @see " + ref); + } + writer.println(" */"); + } + + protected abstract DisassembledInstruction generateExampleInstruction(Template_Type template, List<Argument> arguments) throws AssemblyException; + + private String externalParameters(List< ? extends Parameter> parameters) { + final StringBuilder sb = new StringBuilder(); + boolean first = true; + for (Parameter parameter : parameters) { + if (!ExternalMnemonicSuffixArgument.class.isAssignableFrom(parameter.type())) { + if (!first) { + sb.append(", "); + } + sb.append("<i>").append(parameter.variableName()).append("</i>"); + first = false; + } + } + return sb.toString(); + } + + private String externalMnemonicSuffixes(List< ? extends Parameter> parameters) { + final StringBuilder sb = new StringBuilder(); + for (Parameter parameter : parameters) { + if (ExternalMnemonicSuffixArgument.class.isAssignableFrom(parameter.type())) { + boolean first = true; + String close = "]"; + for (Argument argument : parameter.getLegalTestArguments()) { + final String externalValue = argument.externalValue(); + if (externalValue.length() != 0) { + if (!first) { + sb.append("|"); + } else { + if (((ExternalMnemonicSuffixArgument) argument).isOptional()) { + sb.append("{"); + close = "}"; + } else { + sb.append("["); + } + } + sb.append(externalValue); + first = false; + } + } + sb.append(close); + } + } + return sb.toString(); + } + + private boolean generateRawAssemblerMethods(String rawAssemblerClassName) throws IOException { + Trace.line(1, "Generating raw assembler methods"); + final List<Template_Type> templateList = templates(); + final File sourceFile = getSourceFileFor(rawAssemblerClassName); + ProgramError.check(sourceFile.exists(), "Source file for class containing raw assembler methods does not exist: " + sourceFile); + final CharArraySource charArrayWriter = new CharArraySource((int) sourceFile.length()); + final IndentWriter writer = new IndentWriter(new PrintWriter(charArrayWriter)); + writer.indent(); + + int codeLineCount = 0; + final Map<InstructionDescription, Integer> instructionDescriptions = new HashMap<InstructionDescription, Integer>(); + int maxTemplatesPerDescription = 0; + int i = 0; + for (Template_Type template : templateList) { + printMethodComment(writer, template, i + 1, false); + codeLineCount += printMethod(writer, template); + writer.println(); + + Integer count = instructionDescriptions.get(template.instructionDescription()); + if (count == null) { + count = 1; + } else { + count = count + 1; + } + if (count > maxTemplatesPerDescription) { + maxTemplatesPerDescription = count; + } + instructionDescriptions.put(template.instructionDescription(), count); + i++; + } + final int subroutineCount = printSubroutines(writer); + + writer.outdent(); + writer.close(); + + Trace.line(1, "Generated raw assembler methods" + + " [code line count=" + codeLineCount + ", total line count=" + writer.lineCount() + + ", method count=" + (templateList.size() + subroutineCount) + + ", instruction templates=" + templateList.size() + ", max templates per description=" + maxTemplatesPerDescription + + "]"); + + return Files.updateGeneratedContent(sourceFile, charArrayWriter, "// START GENERATED RAW ASSEMBLER METHODS", "// END GENERATED RAW ASSEMBLER METHODS", false); + } + + /** + * Gets the parameters for a template. + * + * @param forLabelAssemblerMethod + * if true and template contains a label parameter, then this parameter is represented as a + * {@link LabelParameter} object in the returned sequence + */ + protected static List<Parameter> getParameters(Template template, boolean forLabelAssemblerMethod) { + if (!forLabelAssemblerMethod || template.labelParameterIndex() == -1) { + final Class<List<Parameter>> type = null; + return Utils.cast(type, template.parameters()); + } + final List<Parameter> parameters = new ArrayList<Parameter>(template.parameters()); + parameters.set(template.labelParameterIndex(), LabelParameter.LABEL); + return parameters; + } + + protected void printLabelMethodHead(IndentWriter writer, Template_Type template, List<Parameter> parameters) { + writer.print("public void " + template.assemblerMethodName() + "("); + writer.print(formatParameterList("final ", parameters, false)); + writer.println(") {"); + writer.indent(); + } + + /** + * Prints an assembler method for a template that refers to an address via a {@linkplain Label label}. + * + * @param writer the writer to which code will be printed + * @param labelTemplate a template that has a label parameter (i.e. its {@linkplain Template#labelParameterIndex() + * label parameter index} is not -1) + * @param assemblerClassName the name of the class enclosing the assembler method declaration + */ + protected abstract void printLabelMethod(IndentWriter writer, Template_Type labelTemplate, String assemblerClassName); + + /** + * Mechanism that writes the body of the {@link MutableAssembledObject#assemble} method in a generated label method helper class. + */ + public class InstructionWithLabelSubclass { + + final Class<? extends InstructionWithLabel> superClass; + final String name; + final String extraConstructorArguments; + final String labelArgumentPrefix; + + public InstructionWithLabelSubclass(Template template, Class<? extends InstructionWithLabel> superClass, String extraConstructorArguments) { + this.superClass = superClass; + this.name = template.assemblerMethodName() + "_" + template.serial(); + this.extraConstructorArguments = extraConstructorArguments; + final String labelType; + if (superClass == InstructionWithAddress.class) { + labelType = "address"; + } else if (superClass == InstructionWithOffset.class) { + labelType = "offset"; + } else { + throw ProgramError.unexpected("Unknown instruction with label type: " + superClass); + } + this.labelArgumentPrefix = labelType + "As"; + } + + /** + * Prints the body of the {@link MutableAssembledObject#assemble} method in a label method helper class being + * generated by a call to {@link AssemblerGenerator#printLabelMethodHelper}. + * <p> + * The default implementation generates a call to the raw assembler method generated for {@code template} + * + * @param writer + * @param template + */ + protected void printAssembleMethodBody(IndentWriter writer, Template template) { + writer.print(template.assemblerMethodName() + "("); + final List<? extends Parameter> parameters = template.parameters(); + String separator = ""; + int index = 0; + final int labelParameterIndex = template.labelParameterIndex(); + final String labelArgument = labelArgumentPrefix + Strings.firstCharToUpperCase(parameters.get(labelParameterIndex).type().getName()) + "()"; + for (Parameter parameter : parameters) { + writer.print(separator); + if (index == labelParameterIndex) { + writer.print(labelArgument); + } else { + writer.print(parameter.variableName()); + } + separator = ", "; + index++; + } + writer.println(");"); + } + + @Override + public String toString() { + return name; + } + } + + /** + * Prints the code that emits the place holder bytes for a label instruction before a value has been bound to the + * label. + * + * @param writer + * @param template + * @param placeholderInstructionSize the number of place holder bytes written to the instruction stream before the + * label's value has been determined. If this value is -1, then the size depends on the arguments to the + * method and so a call to the raw assembler method is made to determine the size. + * @return an expression denoting the number of place holder bytes emitted + */ + private String printPlaceholderBytes(IndentWriter writer, Template_Type template, int placeholderInstructionSize) { + if (placeholderInstructionSize == -1) { + writer.println("final " + template.parameters().get(template.labelParameterIndex()).type() + " placeHolder = 0;"); + writer.print(template.assemblerMethodName() + "("); + String separator = ""; + for (int i = 0; i < template.parameters().size(); i++) { + writer.print(separator); + if (i == template.labelParameterIndex()) { + writer.print("placeHolder"); + } else { + writer.print(template.parameters().get(i).variableName()); + } + separator = ", "; + } + writer.println(");"); + return "currentPosition() - startPosition"; + } + + if (placeholderInstructionSize == 2) { + writer.println("emitShort(0);"); + } else if (placeholderInstructionSize == 4) { + writer.println("emitInt(0);"); + } else if (placeholderInstructionSize == 8) { + writer.println("emitLong(0);"); + } else { + writer.println("emitZeroes(" + placeholderInstructionSize + ");"); + } + return String.valueOf(placeholderInstructionSize); + } + + /** + * Handles most of the work of {@link #printLabelMethod(IndentWriter, Template, String)}. + * + * @param writer the writer to which code will be printed + * @param template a template that has a label parameter (i.e. its {@linkplain Template#labelParameterIndex() label + * parameter index} is not -1) + * @param parameters the parameters of the template with the label parameter represented as a {@link LabelParameter} + * object + * @param placeholderInstructionSize the number of place holder bytes written to the instruction stream before the + * label's value has been determined. If this value is -1, then the size depends on the arguments to the + * method and so a call to the raw assembler method is made to determine the size. + * @param assemblerClassName the name of the class in which the assembler methods will be declared + * @param labelInstructionSubclassGenerator the object that writes the body of the + * {@link MutableAssembledObject#assemble} method in a generated label method helper class + */ + protected final void printLabelMethodHelper(IndentWriter writer, + Template_Type template, + List<Parameter> parameters, + int placeholderInstructionSize, + String assemblerClassName, + InstructionWithLabelSubclass labelInstructionSubclassGenerator) { + assert template.labelParameterIndex() != -1; + printLabelMethodHead(writer, template, parameters); + writer.println("final int startPosition = currentPosition();"); + final String size = printPlaceholderBytes(writer, template, placeholderInstructionSize); + writer.print("new " + labelInstructionSubclassGenerator.name + "(startPosition, " + size + ", "); + for (Parameter parameter : parameters) { + if (!(parameter instanceof LabelParameter)) { + writer.print(parameter.variableName() + ", "); + } + } + writer.println("label);"); + writer.outdent(); + writer.println("}"); + writer.println(); + + final StringWriter stringWriter = new StringWriter(); + final IndentWriter indentWriter = new IndentWriter(new PrintWriter(stringWriter)); + indentWriter.indent(); + printLabelMethodHelperClass( + indentWriter, + template, + parameters, + assemblerClassName, + labelInstructionSubclassGenerator); + labelMethodHelperClasses.add(stringWriter.toString()); + } + + private final List<String> labelMethodHelperClasses = new ArrayList<String>(); + + private void printLabelMethodHelperClass( + IndentWriter writer, + Template_Type template, + List<Parameter> parameters, + String assemblerClassName, + InstructionWithLabelSubclass labelInstructionSubclass) { + final String simpleAssemblerClassName = assemblerClassName.substring(assemblerClassName.lastIndexOf('.') + 1); + writer.println("class " + labelInstructionSubclass + " extends " + labelInstructionSubclass.superClass.getSimpleName() + " {"); + writer.indent(); + String parametersDecl = ""; + for (Parameter parameter : parameters) { + if (!(parameter instanceof LabelParameter)) { + final Class parameterType = parameter.type(); + final String typeName = Classes.getSimpleName(parameterType, true); + final String variableName = parameter.variableName(); + writer.println("private final " + typeName + " " + variableName + ";"); + parametersDecl = parametersDecl + typeName + " " + variableName + ", "; + } + } + + writer.println(labelInstructionSubclass + "(int startPosition, int endPosition, " + parametersDecl + "Label label) {"); + writer.indent(); + writer.println("super(" + simpleAssemblerClassName + ".this, startPosition, currentPosition(), label" + labelInstructionSubclass.extraConstructorArguments + ");"); + for (Parameter parameter : parameters) { + if (!(parameter instanceof LabelParameter)) { + final String variableName = parameter.variableName(); + writer.println("this." + variableName + " = " + variableName + ";"); + } + } + writer.outdent(); + writer.println("}"); + writer.println("@Override"); + writer.println("protected void assemble() throws AssemblyException {"); + writer.indent(); + labelInstructionSubclass.printAssembleMethodBody(writer, template); + writer.outdent(); + writer.println("}"); + writer.outdent(); + writer.println("}"); + writer.println(); + } + + private boolean generateLabelAssemblerMethods(String labelAssemblerClassName) throws IOException { + Trace.line(1, "Generating label assembler methods"); + final List<Template_Type> labelTemplateList = labelTemplates(); + final File sourceFile = getSourceFileFor(labelAssemblerClassName); + ProgramError.check(sourceFile.exists(), "Source file for class containing label assembler methods does not exist: " + sourceFile); + final CharArraySource charArrayWriter = new CharArraySource((int) sourceFile.length()); + final IndentWriter writer = new IndentWriter(new PrintWriter(charArrayWriter)); + writer.indent(); + + int codeLineCount = 0; + int i = 0; + for (Template_Type labelTemplate : labelTemplateList) { + if (!omitLabelTemplate(labelTemplate)) { + printMethodComment(writer, labelTemplate, i + 1, true); + final int startLineCount = writer.lineCount(); + printLabelMethod(writer, labelTemplate, labelAssemblerClassName); + codeLineCount += writer.lineCount() - startLineCount; + i++; + } + } + writer.outdent(); + + for (String labelMethodHelperClass : labelMethodHelperClasses) { + writer.print(labelMethodHelperClass); + } + + writer.close(); + + Trace.line(1, "Generated label assembler methods" + + " [code line count=" + codeLineCount + + ", total line count=" + writer.lineCount() + + ", method count=" + templates().size() + ")"); + + return Files.updateGeneratedContent(sourceFile, charArrayWriter, "// START GENERATED LABEL ASSEMBLER METHODS", "// END GENERATED LABEL ASSEMBLER METHODS", false); + } + + protected void emitByte(IndentWriter writer, String byteValue) { + writer.print("emitByte(" + byteValue + ");"); + } + + protected void emitByte(IndentWriter writer, byte value) { + emitByte(writer, "((byte) " + Bytes.toHexLiteral(value) + ")"); + } + + protected void generate() { + try { + final String rawAssemblerClassName = rawAssemblerClassNameOption.getValue(); + final String labelAssemblerClassName = labelAssemblerClassNameOption.getValue(); + + final boolean rawAssemblerMethodsUpdated = generateRawAssemblerMethods(rawAssemblerClassName); + final boolean labelAssemblerMethodsUpdated = generateLabelAssemblerMethods(labelAssemblerClassName); + + if (rawAssemblerClassName.equals(labelAssemblerClassName)) { + if (rawAssemblerMethodsUpdated || labelAssemblerMethodsUpdated) { + System.out.println("modified: " + getSourceFileFor(rawAssemblerClassName)); + if (!ToolChain.compile(AssemblerGenerator.class, rawAssemblerClassName)) { + List<Template_Type> allTemplates = new ArrayList<Template_Type>(templates()); + allTemplates.addAll(labelTemplates()); + throw ProgramError.unexpected("compilation failed for: " + rawAssemblerClassName + + "[Maybe missing an import statement for one of the following packages: " + + getImportPackages(rawAssemblerClassName, allTemplates)); + } + } else { + System.out.println("unmodified: " + getSourceFileFor(rawAssemblerClassName)); + } + } else { + if (rawAssemblerMethodsUpdated) { + System.out.println("modified: " + getSourceFileFor(rawAssemblerClassName)); + if (!ToolChain.compile(AssemblerGenerator.class, rawAssemblerClassName)) { + throw ProgramError.unexpected("compilation failed for: " + rawAssemblerClassName + + "[Maybe missing an import statement for one of the following packages: " + + getImportPackages(rawAssemblerClassName, templates())); + } + } else { + System.out.println("unmodified: " + getSourceFileFor(rawAssemblerClassName)); + } + + if (labelAssemblerMethodsUpdated) { + System.out.println("modified: " + getSourceFileFor(labelAssemblerClassName)); + if (!ToolChain.compile(AssemblerGenerator.class, labelAssemblerClassName)) { + throw ProgramError.unexpected("compilation failed for: " + labelAssemblerClassName + + "[Maybe missing an import statement for one of the following packages: " + + getImportPackages(labelAssemblerClassName, labelTemplates())); + } + } else { + System.out.println("unmodified: " + getSourceFileFor(labelAssemblerClassName)); + } + + } + + Trace.line(1, "done"); + } catch (Throwable throwable) { + throwable.printStackTrace(); + System.err.println("something went wrong: " + throwable + ": " + throwable.getMessage()); + } + } + +}