Mercurial > hg > truffle
comparison 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 |
comparison
equal
deleted
inserted
replaced
3732:3e2e8b8abdaf | 3733:e233f5660da4 |
---|---|
1 /* | |
2 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. | |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
4 * | |
5 * This code is free software; you can redistribute it and/or modify it | |
6 * under the terms of the GNU General Public License version 2 only, as | |
7 * published by the Free Software Foundation. | |
8 * | |
9 * This code is distributed in the hope that it will be useful, but WITHOUT | |
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
12 * version 2 for more details (a copy is included in the LICENSE file that | |
13 * accompanied this code). | |
14 * | |
15 * You should have received a copy of the GNU General Public License version | |
16 * 2 along with this work; if not, write to the Free Software Foundation, | |
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
18 * | |
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
20 * or visit www.oracle.com if you need additional information or have any | |
21 * questions. | |
22 */ | |
23 package com.sun.max.asm.gen; | |
24 | |
25 import static com.sun.max.lang.Classes.*; | |
26 | |
27 import java.io.*; | |
28 import java.lang.reflect.*; | |
29 import java.util.*; | |
30 | |
31 import com.sun.max.*; | |
32 import com.sun.max.asm.*; | |
33 import com.sun.max.asm.dis.*; | |
34 import com.sun.max.ide.*; | |
35 import com.sun.max.io.*; | |
36 import com.sun.max.lang.*; | |
37 import com.sun.max.program.*; | |
38 import com.sun.max.program.option.*; | |
39 | |
40 /** | |
41 * Source code generator for raw and label assembler methods derived from an ISA specification. | |
42 */ | |
43 public abstract class AssemblerGenerator<Template_Type extends Template> { | |
44 | |
45 protected OptionSet options = new OptionSet(); | |
46 | |
47 public final Option<File> outputDirectoryOption = options.newFileOption("d", JavaProject.findSourceDirectory(AssemblerGenerator.class), | |
48 "Source directory of the class(es) containing the for generated assembler methods."); | |
49 public final Option<String> assemblerInterfaceNameOption = options.newStringOption("i", null, | |
50 "Interface used to constrain which assembler methods will be generated. " + | |
51 "If absent, an assembler method is generated for each template in the specification."); | |
52 public final Option<String> rawAssemblerClassNameOption = options.newStringOption("r", null, | |
53 "Class containing the generated raw assembler methods."); | |
54 public final Option<String> labelAssemblerClassNameOption = options.newStringOption("l", null, | |
55 "Class containing the generated label assembler methods."); | |
56 public final Option<Boolean> generateRedundantInstructionsOption = options.newBooleanOption("redundant", true, | |
57 "Generate assembler methods for redundant templates. Two templates are redundant if they " + | |
58 "both have the same name and operands. Redundant pairs of instructions are assumed to " + | |
59 "implement the same machine instruction semantics but may have different encodings."); | |
60 | |
61 private final Assembly<Template_Type> assembly; | |
62 private final boolean sortAssemblerMethods; | |
63 private List<Template_Type> templates; | |
64 private List<Template_Type> labelTemplates; | |
65 | |
66 protected AssemblerGenerator(Assembly<Template_Type> assembly, boolean sortAssemblerMethods) { | |
67 Trace.addTo(options); | |
68 this.assembly = assembly; | |
69 final String isa = assembly.isa().name(); | |
70 final String defaultOutputPackage = getPackageName(Assembler.class) + "." + isa.toLowerCase() + ".complete"; | |
71 this.rawAssemblerClassNameOption.setDefaultValue(defaultOutputPackage + "." + isa + "RawAssembler"); | |
72 this.labelAssemblerClassNameOption.setDefaultValue(defaultOutputPackage + "." + isa + "LabelAssembler"); | |
73 this.sortAssemblerMethods = sortAssemblerMethods; | |
74 } | |
75 | |
76 public Assembly<Template_Type> assembly() { | |
77 return assembly; | |
78 } | |
79 | |
80 static class MethodKey { | |
81 final String name; | |
82 final Class[] parameterTypes; | |
83 | |
84 MethodKey(Method method) { | |
85 name = method.getName(); | |
86 parameterTypes = method.getParameterTypes(); | |
87 } | |
88 | |
89 MethodKey(Template template, boolean asLabelTemplate) { | |
90 name = template.assemblerMethodName(); | |
91 parameterTypes = template.parameterTypes(); | |
92 if (asLabelTemplate) { | |
93 final int labelParameterIndex = template.labelParameterIndex(); | |
94 assert labelParameterIndex != -1; | |
95 parameterTypes[labelParameterIndex] = Label.class; | |
96 } | |
97 } | |
98 | |
99 @Override | |
100 public boolean equals(Object object) { | |
101 if (object instanceof MethodKey) { | |
102 final MethodKey other = (MethodKey) object; | |
103 return other.name.equals(name) && Arrays.equals(parameterTypes, other.parameterTypes); | |
104 } | |
105 return false; | |
106 } | |
107 | |
108 @Override | |
109 public int hashCode() { | |
110 return name.hashCode() ^ parameterTypes.length; | |
111 } | |
112 | |
113 @Override | |
114 public String toString() { | |
115 final String paramTypes = Arrays.toString(this.parameterTypes); | |
116 return name + "(" + paramTypes.substring(1, paramTypes.length() - 1) + ")"; | |
117 } | |
118 | |
119 } | |
120 | |
121 private List<Template_Type> filterTemplates(List<Template_Type> templates) { | |
122 if (!generateRedundantInstructionsOption.getValue()) { | |
123 final List<Template_Type> result = new LinkedList<Template_Type>(); | |
124 for (Template_Type template : templates) { | |
125 if (!template.isRedundant()) { | |
126 result.add(template); | |
127 } | |
128 } | |
129 return result; | |
130 } | |
131 return templates; | |
132 } | |
133 | |
134 /** | |
135 * Initializes the set of label and raw templates that will be generated as assembler methods. | |
136 * This includes doing any filtering out of templates based on an {@linkplain #assemblerInterfaceNameOption assembler interface}. | |
137 */ | |
138 private void initTemplates() { | |
139 assert (labelTemplates == null) == (templates == null); | |
140 if (templates == null) { | |
141 final String assemblerInterfaceName = assemblerInterfaceNameOption.getValue(); | |
142 if (assemblerInterfaceName == null) { | |
143 templates = filterTemplates(assembly().templates()); | |
144 labelTemplates = filterTemplates(assembly().labelTemplates()); | |
145 } else { | |
146 final List<Template_Type> newTemplates = new ArrayList<Template_Type>(); | |
147 final List<Template_Type> newLabelTemplates = new ArrayList<Template_Type>(); | |
148 | |
149 Class assemberInterface = null; | |
150 try { | |
151 assemberInterface = Class.forName(assemblerInterfaceName); | |
152 ProgramError.check(assemberInterface.isInterface(), "The class " + assemblerInterfaceName + " is not an interface"); | |
153 } catch (ClassNotFoundException e) { | |
154 throw ProgramError.unexpected("The assembler interface class " + assemblerInterfaceName + " must be on the class path"); | |
155 } | |
156 final Set<MethodKey> assemblerInterfaceMethods = new HashSet<MethodKey>(); | |
157 for (Method assemblerInterfaceMethod : assemberInterface.getDeclaredMethods()) { | |
158 assemblerInterfaceMethods.add(new MethodKey(assemblerInterfaceMethod)); | |
159 } | |
160 | |
161 for (Template_Type labelTemplate : assembly().labelTemplates()) { | |
162 if (assemblerInterfaceMethods.contains(new MethodKey(labelTemplate, true))) { | |
163 assert labelTemplate.labelParameterIndex() != -1; | |
164 newLabelTemplates.add(labelTemplate); | |
165 } | |
166 } | |
167 | |
168 for (Template_Type template : assembly().templates()) { | |
169 if (template.labelParameterIndex() != -1 && assemblerInterfaceMethods.contains(new MethodKey(template, true))) { | |
170 newTemplates.add(template); | |
171 } else if (assemblerInterfaceMethods.contains(new MethodKey(template, false))) { | |
172 newTemplates.add(template); | |
173 } | |
174 } | |
175 | |
176 this.templates = newTemplates; | |
177 this.labelTemplates = newLabelTemplates; | |
178 | |
179 Trace.line(1, "Based on " + assemberInterface + ", " + (assembly().templates().size() - newTemplates.size()) + " (of " + assembly().templates().size() + ") raw templates and " + | |
180 (assembly().labelTemplates().size() - newLabelTemplates.size()) + " (of " + assembly().labelTemplates().size() + ") label templates will be omitted from generated assembler methods"); | |
181 } | |
182 | |
183 if (sortAssemblerMethods) { | |
184 Class<Template_Type[]> type = null; | |
185 Template_Type[] sortedTemplates = Utils.cast(type, Array.newInstance(assembly().templateType(), templates.size())); | |
186 Template_Type[] sortedLabelTemplates = Utils.cast(type, Array.newInstance(assembly().templateType(), labelTemplates.size())); | |
187 templates.toArray(sortedTemplates); | |
188 Arrays.sort(sortedTemplates); | |
189 templates = Arrays.asList(sortedTemplates); | |
190 labelTemplates.toArray(sortedLabelTemplates); | |
191 Arrays.sort(sortedLabelTemplates); | |
192 labelTemplates = Arrays.asList(sortedLabelTemplates); | |
193 } | |
194 } | |
195 } | |
196 | |
197 protected final List<Template_Type> templates() { | |
198 initTemplates(); | |
199 return templates; | |
200 } | |
201 | |
202 protected final List<Template_Type> labelTemplates() { | |
203 initTemplates(); | |
204 return labelTemplates; | |
205 } | |
206 | |
207 /** | |
208 * Gets the absolute path to the source file that will updated to include the generated assembler methods. | |
209 * | |
210 * @param className the name of the Java class that contains the generated assembler methods | |
211 */ | |
212 private File getSourceFileFor(String className) { | |
213 return new File(outputDirectoryOption.getValue(), className.replace('.', File.separatorChar) + ".java").getAbsoluteFile(); | |
214 } | |
215 | |
216 protected final String formatParameterList(String separator, List<? extends Parameter> parameters, boolean typesOnly) { | |
217 String sep = separator; | |
218 final StringBuilder sb = new StringBuilder(); | |
219 for (Parameter parameter : parameters) { | |
220 sb.append(sep); | |
221 sb.append(Classes.getSimpleName(parameter.type(), true)); | |
222 if (!typesOnly) { | |
223 sb.append(" "); | |
224 sb.append(parameter.variableName()); | |
225 } | |
226 if (!sep.startsWith(", ")) { | |
227 sep = ", " + sep; | |
228 } | |
229 } | |
230 return sb.toString(); | |
231 } | |
232 | |
233 /** | |
234 * Prints the source code for the raw assembler method for to a given template. | |
235 * | |
236 * @return the number of source code lines printed | |
237 */ | |
238 protected abstract int printMethod(IndentWriter writer, Template_Type template); | |
239 | |
240 /** | |
241 * Prints the source code for support methods that are used by the methods printed by {@link #printMethod(IndentWriter, Template)}. | |
242 * | |
243 * @return the number of subroutines printed | |
244 */ | |
245 protected int printSubroutines(IndentWriter writer) { | |
246 return 0; | |
247 } | |
248 | |
249 /** | |
250 * Gets the set of packages that must be imported for the generated code to compile successfully. | |
251 * | |
252 * @param className the name of the Java class that contains the assembler methods generated from {@code templates} | |
253 * @param templateList the list of templates for which code is being generated | |
254 * @return a set of packages sorted by name | |
255 */ | |
256 public Set<String> getImportPackages(String className, Iterable<Template_Type> templateList) { | |
257 final String outputPackage = getPackageName(className); | |
258 final Set<String> packages = new TreeSet<String>(); | |
259 packages.add(getPackageName(AssemblyException.class)); | |
260 packages.add(getPackageName(Label.class)); | |
261 for (Template_Type template : templateList) { | |
262 for (Parameter parameter : template.parameters()) { | |
263 final Class type = parameter.type(); | |
264 if (!type.isPrimitive()) { | |
265 final String p = getPackageName(type); | |
266 if (!p.equals(outputPackage)) { | |
267 packages.add(p); | |
268 } | |
269 } | |
270 } | |
271 } | |
272 return packages; | |
273 } | |
274 | |
275 /** | |
276 * Prints the Javadoc comment for a template followed by a C++ style comment stating the template's number (it's | |
277 * position in the order of emitted assembler methods) and its serial (a unique identifier given to every template). | |
278 */ | |
279 protected void printMethodComment(IndentWriter writer, Template_Type template, int number, boolean forLabelAssemblerMethod) { | |
280 printMethodJavadoc(writer, template, forLabelAssemblerMethod); | |
281 writer.println("// Template#: " + number + ", Serial#: " + template.serial()); | |
282 } | |
283 | |
284 /** | |
285 * Determines if a given label template should be omitted from assembler method generation. | |
286 * This method is overridden by subclasses that may generate the code for 2 related label templates | |
287 * in a single assembler method. For example, on X86 most branch instructions can take offsets of variable bit widths | |
288 * and the logic for decoding the bit width of a {@link Label} value may be generated in a single assembler method. | |
289 * <p> | |
290 * The default implementation of this method returns {@code false}. | |
291 */ | |
292 protected boolean omitLabelTemplate(Template_Type labelTemplate) { | |
293 return false; | |
294 } | |
295 | |
296 /** | |
297 * Gets a reference to the architecture manual section describing the given template. The | |
298 * returned string should conform to the format of the {@code @see} Javadoc tag. | |
299 */ | |
300 protected String getJavadocManualReference(Template_Type template) { | |
301 return null; | |
302 } | |
303 | |
304 /** | |
305 * Allows subclasses to print ISA specific details for a template. For example, RISC synthetic instructions | |
306 * print what raw instruction they are derived from. | |
307 * | |
308 * @param extraLinks | |
309 * a sequence to which extra javadoc links should be appended | |
310 */ | |
311 protected void printExtraMethodJavadoc(IndentWriter writer, Template_Type template, List<String> extraLinks, boolean forLabelAssemblerMethod) { | |
312 } | |
313 | |
314 private boolean seenNoSuchAssemblerMethodError; | |
315 | |
316 /** | |
317 * Writes the Javadoc comment for an assembler method. | |
318 * | |
319 * @param template the template from which the assembler method is generated | |
320 */ | |
321 protected void printMethodJavadoc(IndentWriter writer, Template_Type template, boolean forLabelAssemblerMethod) { | |
322 final List<String> extraLinks = new LinkedList<String>(); | |
323 final List<? extends Parameter> parameters = getParameters(template, forLabelAssemblerMethod); | |
324 writer.println("/**"); | |
325 writer.println(" * Pseudo-external assembler syntax: {@code " + template.externalName() + externalMnemonicSuffixes(parameters) + " }" + externalParameters(parameters)); | |
326 | |
327 final boolean printExampleInstruction = true; | |
328 if (printExampleInstruction) { | |
329 | |
330 final List<Argument> arguments = new ArrayList<Argument>(); | |
331 final AddressMapper addressMapper = new AddressMapper(); | |
332 for (Parameter p : template.parameters()) { | |
333 final Argument exampleArg = p.getExampleArgument(); | |
334 if (exampleArg != null) { | |
335 arguments.add(exampleArg); | |
336 } else { | |
337 break; | |
338 } | |
339 } | |
340 if (arguments.size() == template.parameters().size()) { | |
341 try { | |
342 final DisassembledInstruction instruction = generateExampleInstruction(template, arguments); | |
343 final ImmediateArgument targetAddress = instruction.targetAddress(); | |
344 | |
345 if (targetAddress != null) { | |
346 addressMapper.add(targetAddress, "L1"); | |
347 } | |
348 final String exampleInstruction = instruction.toString(addressMapper); | |
349 writer.println(" * Example disassembly syntax: {@code " + exampleInstruction + "}"); | |
350 } catch (NoSuchAssemblerMethodError e) { | |
351 if (!seenNoSuchAssemblerMethodError) { | |
352 seenNoSuchAssemblerMethodError = true; | |
353 ProgramWarning.message("Once generated assembler has been compiled, re-generate it you want a usage example " + | |
354 "in the Javadoc for every generated assembler method"); | |
355 } | |
356 } catch (AssemblyException e) { | |
357 ProgramWarning.message("Error generating example instruction: " + e); | |
358 } | |
359 } | |
360 } | |
361 | |
362 printExtraMethodJavadoc(writer, template, extraLinks, forLabelAssemblerMethod); | |
363 final List<InstructionConstraint> constraints = new ArrayList<InstructionConstraint>(template.instructionDescription().specifications().size()); | |
364 for (Object s : template.instructionDescription().specifications()) { | |
365 if (s instanceof InstructionConstraint) { | |
366 constraints.add((InstructionConstraint) s); | |
367 } | |
368 } | |
369 if (!constraints.isEmpty()) { | |
370 writer.println(" * <p>"); | |
371 for (InstructionConstraint constraint : constraints) { | |
372 final Method predicateMethod = constraint.predicateMethod(); | |
373 if (predicateMethod != null) { | |
374 extraLinks.add(predicateMethod.getDeclaringClass().getName() + "#" + predicateMethod.getName()); | |
375 } | |
376 writer.println(" * Constraint: {@code " + constraint.asJavaExpression() + "}<br />"); | |
377 } | |
378 } | |
379 | |
380 if (!extraLinks.isEmpty()) { | |
381 writer.println(" *"); | |
382 for (String link : extraLinks) { | |
383 writer.println(" * @see " + link); | |
384 } | |
385 } | |
386 | |
387 final String ref = getJavadocManualReference(template); | |
388 if (ref != null) { | |
389 writer.println(" *"); | |
390 writer.println(" * @see " + ref); | |
391 } | |
392 writer.println(" */"); | |
393 } | |
394 | |
395 protected abstract DisassembledInstruction generateExampleInstruction(Template_Type template, List<Argument> arguments) throws AssemblyException; | |
396 | |
397 private String externalParameters(List< ? extends Parameter> parameters) { | |
398 final StringBuilder sb = new StringBuilder(); | |
399 boolean first = true; | |
400 for (Parameter parameter : parameters) { | |
401 if (!ExternalMnemonicSuffixArgument.class.isAssignableFrom(parameter.type())) { | |
402 if (!first) { | |
403 sb.append(", "); | |
404 } | |
405 sb.append("<i>").append(parameter.variableName()).append("</i>"); | |
406 first = false; | |
407 } | |
408 } | |
409 return sb.toString(); | |
410 } | |
411 | |
412 private String externalMnemonicSuffixes(List< ? extends Parameter> parameters) { | |
413 final StringBuilder sb = new StringBuilder(); | |
414 for (Parameter parameter : parameters) { | |
415 if (ExternalMnemonicSuffixArgument.class.isAssignableFrom(parameter.type())) { | |
416 boolean first = true; | |
417 String close = "]"; | |
418 for (Argument argument : parameter.getLegalTestArguments()) { | |
419 final String externalValue = argument.externalValue(); | |
420 if (externalValue.length() != 0) { | |
421 if (!first) { | |
422 sb.append("|"); | |
423 } else { | |
424 if (((ExternalMnemonicSuffixArgument) argument).isOptional()) { | |
425 sb.append("{"); | |
426 close = "}"; | |
427 } else { | |
428 sb.append("["); | |
429 } | |
430 } | |
431 sb.append(externalValue); | |
432 first = false; | |
433 } | |
434 } | |
435 sb.append(close); | |
436 } | |
437 } | |
438 return sb.toString(); | |
439 } | |
440 | |
441 private boolean generateRawAssemblerMethods(String rawAssemblerClassName) throws IOException { | |
442 Trace.line(1, "Generating raw assembler methods"); | |
443 final List<Template_Type> templateList = templates(); | |
444 final File sourceFile = getSourceFileFor(rawAssemblerClassName); | |
445 ProgramError.check(sourceFile.exists(), "Source file for class containing raw assembler methods does not exist: " + sourceFile); | |
446 final CharArraySource charArrayWriter = new CharArraySource((int) sourceFile.length()); | |
447 final IndentWriter writer = new IndentWriter(new PrintWriter(charArrayWriter)); | |
448 writer.indent(); | |
449 | |
450 int codeLineCount = 0; | |
451 final Map<InstructionDescription, Integer> instructionDescriptions = new HashMap<InstructionDescription, Integer>(); | |
452 int maxTemplatesPerDescription = 0; | |
453 int i = 0; | |
454 for (Template_Type template : templateList) { | |
455 printMethodComment(writer, template, i + 1, false); | |
456 codeLineCount += printMethod(writer, template); | |
457 writer.println(); | |
458 | |
459 Integer count = instructionDescriptions.get(template.instructionDescription()); | |
460 if (count == null) { | |
461 count = 1; | |
462 } else { | |
463 count = count + 1; | |
464 } | |
465 if (count > maxTemplatesPerDescription) { | |
466 maxTemplatesPerDescription = count; | |
467 } | |
468 instructionDescriptions.put(template.instructionDescription(), count); | |
469 i++; | |
470 } | |
471 final int subroutineCount = printSubroutines(writer); | |
472 | |
473 writer.outdent(); | |
474 writer.close(); | |
475 | |
476 Trace.line(1, "Generated raw assembler methods" + | |
477 " [code line count=" + codeLineCount + ", total line count=" + writer.lineCount() + | |
478 ", method count=" + (templateList.size() + subroutineCount) + | |
479 ", instruction templates=" + templateList.size() + ", max templates per description=" + maxTemplatesPerDescription + | |
480 "]"); | |
481 | |
482 return Files.updateGeneratedContent(sourceFile, charArrayWriter, "// START GENERATED RAW ASSEMBLER METHODS", "// END GENERATED RAW ASSEMBLER METHODS", false); | |
483 } | |
484 | |
485 /** | |
486 * Gets the parameters for a template. | |
487 * | |
488 * @param forLabelAssemblerMethod | |
489 * if true and template contains a label parameter, then this parameter is represented as a | |
490 * {@link LabelParameter} object in the returned sequence | |
491 */ | |
492 protected static List<Parameter> getParameters(Template template, boolean forLabelAssemblerMethod) { | |
493 if (!forLabelAssemblerMethod || template.labelParameterIndex() == -1) { | |
494 final Class<List<Parameter>> type = null; | |
495 return Utils.cast(type, template.parameters()); | |
496 } | |
497 final List<Parameter> parameters = new ArrayList<Parameter>(template.parameters()); | |
498 parameters.set(template.labelParameterIndex(), LabelParameter.LABEL); | |
499 return parameters; | |
500 } | |
501 | |
502 protected void printLabelMethodHead(IndentWriter writer, Template_Type template, List<Parameter> parameters) { | |
503 writer.print("public void " + template.assemblerMethodName() + "("); | |
504 writer.print(formatParameterList("final ", parameters, false)); | |
505 writer.println(") {"); | |
506 writer.indent(); | |
507 } | |
508 | |
509 /** | |
510 * Prints an assembler method for a template that refers to an address via a {@linkplain Label label}. | |
511 * | |
512 * @param writer the writer to which code will be printed | |
513 * @param labelTemplate a template that has a label parameter (i.e. its {@linkplain Template#labelParameterIndex() | |
514 * label parameter index} is not -1) | |
515 * @param assemblerClassName the name of the class enclosing the assembler method declaration | |
516 */ | |
517 protected abstract void printLabelMethod(IndentWriter writer, Template_Type labelTemplate, String assemblerClassName); | |
518 | |
519 /** | |
520 * Mechanism that writes the body of the {@link MutableAssembledObject#assemble} method in a generated label method helper class. | |
521 */ | |
522 public class InstructionWithLabelSubclass { | |
523 | |
524 final Class<? extends InstructionWithLabel> superClass; | |
525 final String name; | |
526 final String extraConstructorArguments; | |
527 final String labelArgumentPrefix; | |
528 | |
529 public InstructionWithLabelSubclass(Template template, Class<? extends InstructionWithLabel> superClass, String extraConstructorArguments) { | |
530 this.superClass = superClass; | |
531 this.name = template.assemblerMethodName() + "_" + template.serial(); | |
532 this.extraConstructorArguments = extraConstructorArguments; | |
533 final String labelType; | |
534 if (superClass == InstructionWithAddress.class) { | |
535 labelType = "address"; | |
536 } else if (superClass == InstructionWithOffset.class) { | |
537 labelType = "offset"; | |
538 } else { | |
539 throw ProgramError.unexpected("Unknown instruction with label type: " + superClass); | |
540 } | |
541 this.labelArgumentPrefix = labelType + "As"; | |
542 } | |
543 | |
544 /** | |
545 * Prints the body of the {@link MutableAssembledObject#assemble} method in a label method helper class being | |
546 * generated by a call to {@link AssemblerGenerator#printLabelMethodHelper}. | |
547 * <p> | |
548 * The default implementation generates a call to the raw assembler method generated for {@code template} | |
549 * | |
550 * @param writer | |
551 * @param template | |
552 */ | |
553 protected void printAssembleMethodBody(IndentWriter writer, Template template) { | |
554 writer.print(template.assemblerMethodName() + "("); | |
555 final List<? extends Parameter> parameters = template.parameters(); | |
556 String separator = ""; | |
557 int index = 0; | |
558 final int labelParameterIndex = template.labelParameterIndex(); | |
559 final String labelArgument = labelArgumentPrefix + Strings.firstCharToUpperCase(parameters.get(labelParameterIndex).type().getName()) + "()"; | |
560 for (Parameter parameter : parameters) { | |
561 writer.print(separator); | |
562 if (index == labelParameterIndex) { | |
563 writer.print(labelArgument); | |
564 } else { | |
565 writer.print(parameter.variableName()); | |
566 } | |
567 separator = ", "; | |
568 index++; | |
569 } | |
570 writer.println(");"); | |
571 } | |
572 | |
573 @Override | |
574 public String toString() { | |
575 return name; | |
576 } | |
577 } | |
578 | |
579 /** | |
580 * Prints the code that emits the place holder bytes for a label instruction before a value has been bound to the | |
581 * label. | |
582 * | |
583 * @param writer | |
584 * @param template | |
585 * @param placeholderInstructionSize the number of place holder bytes written to the instruction stream before the | |
586 * label's value has been determined. If this value is -1, then the size depends on the arguments to the | |
587 * method and so a call to the raw assembler method is made to determine the size. | |
588 * @return an expression denoting the number of place holder bytes emitted | |
589 */ | |
590 private String printPlaceholderBytes(IndentWriter writer, Template_Type template, int placeholderInstructionSize) { | |
591 if (placeholderInstructionSize == -1) { | |
592 writer.println("final " + template.parameters().get(template.labelParameterIndex()).type() + " placeHolder = 0;"); | |
593 writer.print(template.assemblerMethodName() + "("); | |
594 String separator = ""; | |
595 for (int i = 0; i < template.parameters().size(); i++) { | |
596 writer.print(separator); | |
597 if (i == template.labelParameterIndex()) { | |
598 writer.print("placeHolder"); | |
599 } else { | |
600 writer.print(template.parameters().get(i).variableName()); | |
601 } | |
602 separator = ", "; | |
603 } | |
604 writer.println(");"); | |
605 return "currentPosition() - startPosition"; | |
606 } | |
607 | |
608 if (placeholderInstructionSize == 2) { | |
609 writer.println("emitShort(0);"); | |
610 } else if (placeholderInstructionSize == 4) { | |
611 writer.println("emitInt(0);"); | |
612 } else if (placeholderInstructionSize == 8) { | |
613 writer.println("emitLong(0);"); | |
614 } else { | |
615 writer.println("emitZeroes(" + placeholderInstructionSize + ");"); | |
616 } | |
617 return String.valueOf(placeholderInstructionSize); | |
618 } | |
619 | |
620 /** | |
621 * Handles most of the work of {@link #printLabelMethod(IndentWriter, Template, String)}. | |
622 * | |
623 * @param writer the writer to which code will be printed | |
624 * @param template a template that has a label parameter (i.e. its {@linkplain Template#labelParameterIndex() label | |
625 * parameter index} is not -1) | |
626 * @param parameters the parameters of the template with the label parameter represented as a {@link LabelParameter} | |
627 * object | |
628 * @param placeholderInstructionSize the number of place holder bytes written to the instruction stream before the | |
629 * label's value has been determined. If this value is -1, then the size depends on the arguments to the | |
630 * method and so a call to the raw assembler method is made to determine the size. | |
631 * @param assemblerClassName the name of the class in which the assembler methods will be declared | |
632 * @param labelInstructionSubclassGenerator the object that writes the body of the | |
633 * {@link MutableAssembledObject#assemble} method in a generated label method helper class | |
634 */ | |
635 protected final void printLabelMethodHelper(IndentWriter writer, | |
636 Template_Type template, | |
637 List<Parameter> parameters, | |
638 int placeholderInstructionSize, | |
639 String assemblerClassName, | |
640 InstructionWithLabelSubclass labelInstructionSubclassGenerator) { | |
641 assert template.labelParameterIndex() != -1; | |
642 printLabelMethodHead(writer, template, parameters); | |
643 writer.println("final int startPosition = currentPosition();"); | |
644 final String size = printPlaceholderBytes(writer, template, placeholderInstructionSize); | |
645 writer.print("new " + labelInstructionSubclassGenerator.name + "(startPosition, " + size + ", "); | |
646 for (Parameter parameter : parameters) { | |
647 if (!(parameter instanceof LabelParameter)) { | |
648 writer.print(parameter.variableName() + ", "); | |
649 } | |
650 } | |
651 writer.println("label);"); | |
652 writer.outdent(); | |
653 writer.println("}"); | |
654 writer.println(); | |
655 | |
656 final StringWriter stringWriter = new StringWriter(); | |
657 final IndentWriter indentWriter = new IndentWriter(new PrintWriter(stringWriter)); | |
658 indentWriter.indent(); | |
659 printLabelMethodHelperClass( | |
660 indentWriter, | |
661 template, | |
662 parameters, | |
663 assemblerClassName, | |
664 labelInstructionSubclassGenerator); | |
665 labelMethodHelperClasses.add(stringWriter.toString()); | |
666 } | |
667 | |
668 private final List<String> labelMethodHelperClasses = new ArrayList<String>(); | |
669 | |
670 private void printLabelMethodHelperClass( | |
671 IndentWriter writer, | |
672 Template_Type template, | |
673 List<Parameter> parameters, | |
674 String assemblerClassName, | |
675 InstructionWithLabelSubclass labelInstructionSubclass) { | |
676 final String simpleAssemblerClassName = assemblerClassName.substring(assemblerClassName.lastIndexOf('.') + 1); | |
677 writer.println("class " + labelInstructionSubclass + " extends " + labelInstructionSubclass.superClass.getSimpleName() + " {"); | |
678 writer.indent(); | |
679 String parametersDecl = ""; | |
680 for (Parameter parameter : parameters) { | |
681 if (!(parameter instanceof LabelParameter)) { | |
682 final Class parameterType = parameter.type(); | |
683 final String typeName = Classes.getSimpleName(parameterType, true); | |
684 final String variableName = parameter.variableName(); | |
685 writer.println("private final " + typeName + " " + variableName + ";"); | |
686 parametersDecl = parametersDecl + typeName + " " + variableName + ", "; | |
687 } | |
688 } | |
689 | |
690 writer.println(labelInstructionSubclass + "(int startPosition, int endPosition, " + parametersDecl + "Label label) {"); | |
691 writer.indent(); | |
692 writer.println("super(" + simpleAssemblerClassName + ".this, startPosition, currentPosition(), label" + labelInstructionSubclass.extraConstructorArguments + ");"); | |
693 for (Parameter parameter : parameters) { | |
694 if (!(parameter instanceof LabelParameter)) { | |
695 final String variableName = parameter.variableName(); | |
696 writer.println("this." + variableName + " = " + variableName + ";"); | |
697 } | |
698 } | |
699 writer.outdent(); | |
700 writer.println("}"); | |
701 writer.println("@Override"); | |
702 writer.println("protected void assemble() throws AssemblyException {"); | |
703 writer.indent(); | |
704 labelInstructionSubclass.printAssembleMethodBody(writer, template); | |
705 writer.outdent(); | |
706 writer.println("}"); | |
707 writer.outdent(); | |
708 writer.println("}"); | |
709 writer.println(); | |
710 } | |
711 | |
712 private boolean generateLabelAssemblerMethods(String labelAssemblerClassName) throws IOException { | |
713 Trace.line(1, "Generating label assembler methods"); | |
714 final List<Template_Type> labelTemplateList = labelTemplates(); | |
715 final File sourceFile = getSourceFileFor(labelAssemblerClassName); | |
716 ProgramError.check(sourceFile.exists(), "Source file for class containing label assembler methods does not exist: " + sourceFile); | |
717 final CharArraySource charArrayWriter = new CharArraySource((int) sourceFile.length()); | |
718 final IndentWriter writer = new IndentWriter(new PrintWriter(charArrayWriter)); | |
719 writer.indent(); | |
720 | |
721 int codeLineCount = 0; | |
722 int i = 0; | |
723 for (Template_Type labelTemplate : labelTemplateList) { | |
724 if (!omitLabelTemplate(labelTemplate)) { | |
725 printMethodComment(writer, labelTemplate, i + 1, true); | |
726 final int startLineCount = writer.lineCount(); | |
727 printLabelMethod(writer, labelTemplate, labelAssemblerClassName); | |
728 codeLineCount += writer.lineCount() - startLineCount; | |
729 i++; | |
730 } | |
731 } | |
732 writer.outdent(); | |
733 | |
734 for (String labelMethodHelperClass : labelMethodHelperClasses) { | |
735 writer.print(labelMethodHelperClass); | |
736 } | |
737 | |
738 writer.close(); | |
739 | |
740 Trace.line(1, "Generated label assembler methods" + | |
741 " [code line count=" + codeLineCount + | |
742 ", total line count=" + writer.lineCount() + | |
743 ", method count=" + templates().size() + ")"); | |
744 | |
745 return Files.updateGeneratedContent(sourceFile, charArrayWriter, "// START GENERATED LABEL ASSEMBLER METHODS", "// END GENERATED LABEL ASSEMBLER METHODS", false); | |
746 } | |
747 | |
748 protected void emitByte(IndentWriter writer, String byteValue) { | |
749 writer.print("emitByte(" + byteValue + ");"); | |
750 } | |
751 | |
752 protected void emitByte(IndentWriter writer, byte value) { | |
753 emitByte(writer, "((byte) " + Bytes.toHexLiteral(value) + ")"); | |
754 } | |
755 | |
756 protected void generate() { | |
757 try { | |
758 final String rawAssemblerClassName = rawAssemblerClassNameOption.getValue(); | |
759 final String labelAssemblerClassName = labelAssemblerClassNameOption.getValue(); | |
760 | |
761 final boolean rawAssemblerMethodsUpdated = generateRawAssemblerMethods(rawAssemblerClassName); | |
762 final boolean labelAssemblerMethodsUpdated = generateLabelAssemblerMethods(labelAssemblerClassName); | |
763 | |
764 if (rawAssemblerClassName.equals(labelAssemblerClassName)) { | |
765 if (rawAssemblerMethodsUpdated || labelAssemblerMethodsUpdated) { | |
766 System.out.println("modified: " + getSourceFileFor(rawAssemblerClassName)); | |
767 if (!ToolChain.compile(AssemblerGenerator.class, rawAssemblerClassName)) { | |
768 List<Template_Type> allTemplates = new ArrayList<Template_Type>(templates()); | |
769 allTemplates.addAll(labelTemplates()); | |
770 throw ProgramError.unexpected("compilation failed for: " + rawAssemblerClassName + | |
771 "[Maybe missing an import statement for one of the following packages: " + | |
772 getImportPackages(rawAssemblerClassName, allTemplates)); | |
773 } | |
774 } else { | |
775 System.out.println("unmodified: " + getSourceFileFor(rawAssemblerClassName)); | |
776 } | |
777 } else { | |
778 if (rawAssemblerMethodsUpdated) { | |
779 System.out.println("modified: " + getSourceFileFor(rawAssemblerClassName)); | |
780 if (!ToolChain.compile(AssemblerGenerator.class, rawAssemblerClassName)) { | |
781 throw ProgramError.unexpected("compilation failed for: " + rawAssemblerClassName + | |
782 "[Maybe missing an import statement for one of the following packages: " + | |
783 getImportPackages(rawAssemblerClassName, templates())); | |
784 } | |
785 } else { | |
786 System.out.println("unmodified: " + getSourceFileFor(rawAssemblerClassName)); | |
787 } | |
788 | |
789 if (labelAssemblerMethodsUpdated) { | |
790 System.out.println("modified: " + getSourceFileFor(labelAssemblerClassName)); | |
791 if (!ToolChain.compile(AssemblerGenerator.class, labelAssemblerClassName)) { | |
792 throw ProgramError.unexpected("compilation failed for: " + labelAssemblerClassName + | |
793 "[Maybe missing an import statement for one of the following packages: " + | |
794 getImportPackages(labelAssemblerClassName, labelTemplates())); | |
795 } | |
796 } else { | |
797 System.out.println("unmodified: " + getSourceFileFor(labelAssemblerClassName)); | |
798 } | |
799 | |
800 } | |
801 | |
802 Trace.line(1, "done"); | |
803 } catch (Throwable throwable) { | |
804 throwable.printStackTrace(); | |
805 System.err.println("something went wrong: " + throwable + ": " + throwable.getMessage()); | |
806 } | |
807 } | |
808 | |
809 } |