001/* 002 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package jdk.internal.jvmci.hotspotvmconfig.processor; 024 025import java.io.*; 026import java.lang.annotation.*; 027import java.util.*; 028import java.util.Map.Entry; 029import java.util.function.*; 030 031import javax.annotation.processing.*; 032import javax.lang.model.*; 033import javax.lang.model.element.*; 034import javax.tools.Diagnostic.Kind; 035import javax.tools.*; 036 037import jdk.internal.jvmci.common.*; 038import jdk.internal.jvmci.hotspotvmconfig.*; 039 040@SupportedAnnotationTypes({ 041 // @formatter:off 042 "jdk.internal.jvmci.hotspotvmconfig.HotSpotVMConstant", 043 "jdk.internal.jvmci.hotspotvmconfig.HotSpotVMFlag", 044 "jdk.internal.jvmci.hotspotvmconfig.HotSpotVMField", 045 "jdk.internal.jvmci.hotspotvmconfig.HotSpotVMType", 046 "jdk.internal.jvmci.hotspotvmconfig.HotSpotVMValue"}) 047 // @formatter:on 048public class HotSpotVMConfigProcessor extends AbstractProcessor { 049 050 public HotSpotVMConfigProcessor() { 051 } 052 053 @Override 054 public SourceVersion getSupportedSourceVersion() { 055 return SourceVersion.latest(); 056 } 057 058 /** 059 * Set to true to enable logging to a local file during annotation processing. There's no normal 060 * channel for any debug messages and debugging annotation processors requires some special 061 * setup. 062 */ 063 private static final boolean DEBUG = false; 064 065 private PrintWriter log; 066 067 /** 068 * Logging facility for debugging the annotation processor. 069 */ 070 071 private PrintWriter getLog() { 072 if (log == null) { 073 try { 074 // Create the log file within the generated source directory so it's easy to find. 075 // /tmp isn't platform independent and java.io.tmpdir can map anywhere, particularly 076 // on the mac. 077 FileObject file = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", getClass().getSimpleName() + "log"); 078 log = new PrintWriter(new FileWriter(file.toUri().getPath(), true)); 079 } catch (IOException e) { 080 // Do nothing 081 } 082 } 083 return log; 084 } 085 086 private void logMessage(String format, Object... args) { 087 if (!DEBUG) { 088 return; 089 } 090 PrintWriter bw = getLog(); 091 if (bw != null) { 092 bw.printf(format, args); 093 bw.flush(); 094 } 095 } 096 097 private void logException(Throwable t) { 098 if (!DEBUG) { 099 return; 100 } 101 PrintWriter bw = getLog(); 102 if (bw != null) { 103 t.printStackTrace(bw); 104 bw.flush(); 105 } 106 } 107 108 /** 109 * Bugs in an annotation processor can cause silent failure so try to report any exception 110 * throws as errors. 111 */ 112 private void reportExceptionThrow(Element element, Throwable t) { 113 if (element != null) { 114 logMessage("throw for %s:\n", element); 115 } 116 logException(t); 117 errorMessage(element, "Exception throw during processing: %s %s", t, Arrays.toString(Arrays.copyOf(t.getStackTrace(), 4))); 118 } 119 120 //@formatter:off 121 String[] prologue = new String[]{ 122 "// The normal wrappers CommandLineFlags::boolAt and CommandLineFlags::intxAt skip constant flags", 123 "static bool boolAt(char* name, bool* value) {", 124 " Flag* result = Flag::find_flag(name, strlen(name), true, true);", 125 " if (result == NULL) return false;", 126 " if (!result->is_bool()) return false;", 127 " *value = result->get_bool();", 128 " return true;", 129 "}", 130 "", 131 "static bool intxAt(char* name, intx* value) {", 132 " Flag* result = Flag::find_flag(name, strlen(name), true, true);", 133 " if (result == NULL) return false;", 134 " if (!result->is_intx()) return false;", 135 " *value = result->get_intx();", 136 " return true;", 137 "}", 138 "", 139 "#define set_boolean(name, value) vmconfig_oop->bool_field_put(fs.offset(), value)", 140 "#define set_byte(name, value) vmconfig_oop->byte_field_put(fs.offset(), (jbyte)(value))", 141 "#define set_short(name, value) vmconfig_oop->short_field_put(fs.offset(), (jshort)(value))", 142 "#define set_int(name, value) vmconfig_oop->int_field_put(fs.offset(), (int)(value))", 143 "#define set_long(name, value) vmconfig_oop->long_field_put(fs.offset(), value)", 144 "#define set_address(name, value) do { set_long(name, (jlong)(value)); } while (0)", 145 "", 146 "#define set_optional_boolean_flag(varName, flagName) do { bool flagValue; if (boolAt((char*) flagName, &flagValue)) { set_boolean(varName, flagValue); } } while (0)", 147 "#define set_optional_int_flag(varName, flagName) do { intx flagValue; if (intxAt((char*) flagName, &flagValue)) { set_int(varName, flagValue); } } while (0)", 148 "#define set_optional_long_flag(varName, flagName) do { intx flagValue; if (intxAt((char*) flagName, &flagValue)) { set_long(varName, flagValue); } } while (0)", 149 "", 150 "void VMStructs::initHotSpotVMConfig(oop vmconfig_oop) {", 151 " InstanceKlass* vmconfig_klass = InstanceKlass::cast(vmconfig_oop->klass());", 152 "", 153 }; 154 //@formatter:on 155 156 String outputName = "HotSpotVMConfig.inline.hpp"; 157 String outputDirectory = "hotspot"; 158 159 private void createFiles(Map<String, VMConfigField> annotations, Element element) { 160 161 Filer filer = processingEnv.getFiler(); 162 try (PrintWriter out = createSourceFile(outputDirectory, outputName, filer, element)) { 163 164 for (String line : prologue) { 165 out.println(line); 166 } 167 168 Map<String, Integer> expectedValues = new HashMap<>(); 169 for (VMConfigField value : annotations.values()) { 170 if (!value.optional) { 171 String key = value.define != null ? value.define : ""; 172 if (expectedValues.get(key) == null) { 173 expectedValues.put(key, 1); 174 } else { 175 expectedValues.put(key, expectedValues.get(key) + 1); 176 } 177 } 178 } 179 180 out.printf(" int expected = %s;%n", expectedValues.get("")); 181 for (Entry<String, Integer> entry : expectedValues.entrySet()) { 182 if (entry.getKey().equals("")) { 183 continue; 184 } 185 out.printf("#if %s%n", entry.getKey()); 186 out.printf(" expected += %s;%n", entry.getValue()); 187 out.printf("#endif%n"); 188 } 189 out.println(" int assigned = 0;"); 190 out.println(" for (JavaFieldStream fs(vmconfig_klass); !fs.done(); fs.next()) {"); 191 192 Set<String> fieldTypes = new HashSet<>(); 193 for (VMConfigField key : annotations.values()) { 194 fieldTypes.add(key.getType()); 195 } 196 // For each type of field, generate a switch on the length of the symbol and then do a 197 // direct compare. In general this reduces each operation to 2 tests plus a string 198 // compare. Being more perfect than that is probably not worth it. 199 for (String type : fieldTypes) { 200 String sigtype = type.equals("boolean") ? "bool" : type; 201 out.println(" if (fs.signature() == vmSymbols::" + sigtype + "_signature()) {"); 202 Set<Integer> lengths = new HashSet<>(); 203 for (Entry<String, VMConfigField> entry : annotations.entrySet()) { 204 if (entry.getValue().getType().equals(type)) { 205 lengths.add(entry.getKey().length()); 206 } 207 } 208 out.println(" switch (fs.name()->utf8_length()) {"); 209 for (int len : lengths) { 210 out.println(" case " + len + ":"); 211 for (Entry<String, VMConfigField> entry : annotations.entrySet()) { 212 if (entry.getValue().getType().equals(type) && entry.getKey().length() == len) { 213 out.println(" if (fs.name()->equals(\"" + entry.getKey() + "\")) {"); 214 entry.getValue().emit(out); 215 out.println(" continue;"); 216 out.println(" }"); 217 } 218 } 219 out.println(" continue;"); 220 } 221 out.println(" } // switch"); 222 out.println(" continue;"); 223 out.println(" } // if"); 224 } 225 out.println(" } // for"); 226 out.println(" guarantee(assigned == expected, \"Didn't find all fields during init of HotSpotVMConfig. Maybe recompile?\");"); 227 out.println("}"); 228 } 229 } 230 231 protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) { 232 try { 233 // Ensure Unix line endings to comply with code style guide checked by Checkstyle 234 FileObject sourceFile = filer.createResource(StandardLocation.SOURCE_OUTPUT, pkg, relativeName, originatingElements); 235 logMessage("%s\n", sourceFile); 236 return new PrintWriter(sourceFile.openWriter()) { 237 238 @Override 239 public void println() { 240 print("\n"); 241 } 242 }; 243 } catch (IOException e) { 244 throw new RuntimeException(e); 245 } 246 } 247 248 static class VMConfigField { 249 final String setter; 250 final String define; 251 private boolean optional; 252 final VariableElement field; 253 254 public VMConfigField(VariableElement field, HotSpotVMField value) { 255 this.field = field; 256 define = archDefines(value.archs()); 257 String type = field.asType().toString(); 258 String name = value.name(); 259 int i = name.lastIndexOf("::"); 260 switch (value.get()) { 261 case OFFSET: 262 setter = String.format("set_%s(\"%s\", offset_of(%s, %s));", type, field.getSimpleName(), name.substring(0, i), name.substring(i + 2)); 263 break; 264 case ADDRESS: 265 setter = String.format("set_address(\"%s\", &%s);", field.getSimpleName(), name); 266 break; 267 case VALUE: 268 setter = String.format("set_%s(\"%s\", (%s) (intptr_t) %s);", type, field.getSimpleName(), type, name); 269 break; 270 default: 271 throw new JVMCIError("unexpected type: " + value.get()); 272 } 273 } 274 275 public VMConfigField(VariableElement field, HotSpotVMType value) { 276 this.field = field; 277 define = null; // ((HotSpotVMType) annotation).archs(); 278 String type = field.asType().toString(); 279 setter = String.format("set_%s(\"%s\", sizeof(%s));", type, field.getSimpleName(), value.name()); 280 } 281 282 public VMConfigField(VariableElement field, HotSpotVMValue value) { 283 this.field = field; 284 String[] defines = value.defines(); 285 int length = defines.length; 286 if (length != 0) { 287 for (int i = 0; i < length; i++) { 288 defines[i] = "defined(" + defines[i] + ")"; 289 } 290 define = String.join(" || ", defines); 291 } else { 292 define = null; // ((HotSpotVMValue) annotation).archs(); 293 } 294 String type = field.asType().toString(); 295 if (value.get() == HotSpotVMValue.Type.ADDRESS) { 296 setter = String.format("set_address(\"%s\", %s);", field.getSimpleName(), value.expression()); 297 } else { 298 setter = String.format("set_%s(\"%s\", %s);", type, field.getSimpleName(), value.expression()); 299 } 300 } 301 302 public VMConfigField(VariableElement field, HotSpotVMConstant value) { 303 this.field = field; 304 define = archDefines(value.archs()); 305 String type = field.asType().toString(); 306 setter = String.format("set_%s(\"%s\", %s);", type, field.getSimpleName(), value.name()); 307 } 308 309 public VMConfigField(VariableElement field, HotSpotVMFlag value) { 310 this.field = field; 311 define = archDefines(value.archs()); 312 optional = value.optional(); 313 String type = field.asType().toString(); 314 if (value.optional()) { 315 setter = String.format("set_optional_%s_flag(\"%s\", \"%s\");", type, field.getSimpleName(), value.name()); 316 } else { 317 setter = String.format("set_%s(\"%s\", %s);", type, field.getSimpleName(), value.name()); 318 } 319 } 320 321 public String getType() { 322 return field.asType().toString(); 323 } 324 325 private static String archDefine(String arch) { 326 switch (arch) { 327 case "amd64": 328 return "defined(AMD64)"; 329 case "sparcv9": 330 return "(defined(SPARC) && defined(_LP64))"; 331 case "sparc": 332 return "defined(SPARC)"; 333 default: 334 throw new JVMCIError("unexpected arch: " + arch); 335 } 336 } 337 338 private static String archDefines(String[] archs) { 339 if (archs == null || archs.length == 0) { 340 return null; 341 } 342 if (archs.length == 1) { 343 return archDefine(archs[0]); 344 } 345 String[] defs = new String[archs.length]; 346 int i = 0; 347 for (String arch : archs) { 348 defs[i++] = archDefine(arch); 349 } 350 return String.join(" || ", defs); 351 } 352 353 public void emit(PrintWriter out) { 354 if (define != null) { 355 out.printf("#if %s\n", define); 356 } 357 out.printf(" %s%n", setter); 358 if (!optional) { 359 out.printf(" assigned++;%n"); 360 } 361 if (define != null) { 362 out.printf("#endif\n"); 363 } 364 } 365 366 } 367 368 @SuppressWarnings("unchecked") 369 private <T extends Annotation> void collectAnnotations(RoundEnvironment roundEnv, Map<String, VMConfigField> annotationMap, Class<T> annotationClass, 370 BiFunction<VariableElement, T, VMConfigField> builder) { 371 for (Element element : roundEnv.getElementsAnnotatedWith(annotationClass)) { 372 Annotation constant = element.getAnnotation(annotationClass); 373 if (element.getKind() != ElementKind.FIELD) { 374 errorMessage(element, "%s annotations may only be on fields", annotationClass.getSimpleName()); 375 } 376 if (annotationClass == HotSpotVMValue.class) { 377 HotSpotVMValue value = (HotSpotVMValue) constant; 378 if (value.get() == HotSpotVMValue.Type.ADDRESS && !element.asType().toString().equals("long")) { 379 errorMessage(element, "HotSpotVMValue with get == ADDRESS must be of type long, but found %s", element.asType()); 380 } 381 } 382 if (currentTypeElement == null) { 383 currentTypeElement = element.getEnclosingElement(); 384 } else { 385 if (!currentTypeElement.equals(element.getEnclosingElement())) { 386 errorMessage(element, "Multiple types encountered. Only HotSpotVMConfig is supported"); 387 } 388 } 389 annotationMap.put(element.getSimpleName().toString(), builder.apply((VariableElement) element, (T) constant)); 390 } 391 } 392 393 private void errorMessage(Element element, String format, Object... args) { 394 processingEnv.getMessager().printMessage(Kind.ERROR, String.format(format, args), element); 395 } 396 397 Element currentTypeElement = null; 398 399 @Override 400 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 401 if (roundEnv.processingOver()) { 402 return true; 403 } 404 logMessage("Starting round %s %s\n", roundEnv, annotations); 405 try { 406 407 currentTypeElement = null; 408 409 // First collect all the annotations. 410 Map<String, VMConfigField> annotationMap = new HashMap<>(); 411 collectAnnotations(roundEnv, annotationMap, HotSpotVMConstant.class, (e, v) -> new VMConfigField(e, v)); 412 collectAnnotations(roundEnv, annotationMap, HotSpotVMFlag.class, (e, v) -> new VMConfigField(e, v)); 413 collectAnnotations(roundEnv, annotationMap, HotSpotVMField.class, (e, v) -> new VMConfigField(e, v)); 414 collectAnnotations(roundEnv, annotationMap, HotSpotVMType.class, (e, v) -> new VMConfigField(e, v)); 415 collectAnnotations(roundEnv, annotationMap, HotSpotVMValue.class, (e, v) -> new VMConfigField(e, v)); 416 417 if (annotationMap.isEmpty()) { 418 return true; 419 } 420 421 logMessage("type element %s\n", currentTypeElement); 422 createFiles(annotationMap, currentTypeElement); 423 424 } catch (Throwable t) { 425 reportExceptionThrow(null, t); 426 } 427 428 return true; 429 } 430}