001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.jikesrvm.tools.header_gen;
014
015import static org.jikesrvm.objectmodel.ThinLockConstants.TL_THREAD_ID_SHIFT;
016import static org.jikesrvm.runtime.ExitStatus.EXIT_STATUS_BAD_WORKING_DIR;
017import static org.jikesrvm.runtime.ExitStatus.EXIT_STATUS_BOGUS_COMMAND_LINE_ARG;
018import static org.jikesrvm.runtime.ExitStatus.EXIT_STATUS_COULD_NOT_EXECUTE;
019import static org.jikesrvm.runtime.ExitStatus.EXIT_STATUS_DYING_WITH_UNCAUGHT_EXCEPTION;
020import static org.jikesrvm.runtime.ExitStatus.EXIT_STATUS_EXECUTABLE_NOT_FOUND;
021import static org.jikesrvm.runtime.ExitStatus.EXIT_STATUS_IMPOSSIBLE_LIBRARY_FUNCTION_ERROR;
022import static org.jikesrvm.runtime.ExitStatus.EXIT_STATUS_JNI_TROUBLE;
023import static org.jikesrvm.runtime.ExitStatus.EXIT_STATUS_MISC_TROUBLE;
024import static org.jikesrvm.runtime.ExitStatus.EXIT_STATUS_SYSCALL_TROUBLE;
025import static org.jikesrvm.runtime.ExitStatus.EXIT_STATUS_TIMER_TROUBLE;
026import static org.jikesrvm.runtime.ExitStatus.EXIT_STATUS_UNEXPECTED_CALL_TO_SYS;
027import static org.jikesrvm.runtime.ExitStatus.EXIT_STATUS_UNSUPPORTED_INTERNAL_OP;
028
029import java.io.FileOutputStream;
030import java.io.IOException;
031import java.io.PrintStream;
032import java.util.Arrays;
033
034import org.jikesrvm.VM;
035import org.jikesrvm.architecture.StackFrameLayout;
036import org.jikesrvm.classloader.RVMClass;
037import org.jikesrvm.classloader.RVMField;
038import org.jikesrvm.classloader.TypeReference;
039import org.jikesrvm.objectmodel.ObjectModel;
040import org.jikesrvm.runtime.ArchEntrypoints;
041import org.jikesrvm.runtime.Entrypoints;
042import org.jikesrvm.runtime.RuntimeEntrypoints;
043import org.jikesrvm.scheduler.RVMThread;
044import org.jikesrvm.util.Services;
045import org.vmmagic.unboxed.Address;
046import org.vmmagic.unboxed.Offset;
047
048/**
049 * Emit a header file containing declarations required to access VM
050 * data structures from C.
051 */
052public class GenerateInterfaceDeclarations {
053
054  static PrintStream out;
055  static final GenArch arch;
056
057  static {
058    GenArch tmp = null;
059    try {
060      tmp =
061          (GenArch) Class.forName(VM.BuildForIA32 ? "org.jikesrvm.tools.header_gen.GenArch_ia32" : "org.jikesrvm.tools.header_gen.GenArch_ppc").newInstance();
062    } catch (Exception e) {
063      e.printStackTrace();
064      System.exit(EXIT_STATUS_MISC_TROUBLE);     // we must *not* go on if the above has failed
065    }
066    arch = tmp;
067  }
068
069  static void p(String s) {
070    out.print(s);
071  }
072
073  static void p(String s, Offset off) {
074    if (VM.BuildFor64Addr) {
075      out.print(s + off.toLong());
076    } else {
077      out.print(s + Services.addressAsHexString(off.toWord().toAddress()));
078    }
079  }
080
081  static void pln(String s) {
082    out.println(s);
083  }
084
085  static void pln(String s, int i) {
086    out.print("#define " + s + " 0x" + Integer.toHexString(i) + "\n");
087  }
088
089  static void pln(String s, Address addr) {
090    out.print("#define " + s + " ((Address)" + Services.addressAsHexString(addr) + ")\n");
091  }
092
093  static void pln(String s, Offset off) {
094    out.print("#define " + s + " ((Offset)" + Services.addressAsHexString(off.toWord().toAddress()) + ")\n");
095  }
096
097  static void pln() {
098    out.println();
099  }
100
101  GenerateInterfaceDeclarations() {
102  }
103
104  static long bootImageDataAddress = 0;
105  static long bootImageCodeAddress = 0;
106  static long bootImageRMapAddress = 0;
107  static String outFileName;
108
109  private static long decodeLong(String s) {
110    if (s.endsWith("L")) {
111      s = s.substring(0, s.length() - 1);
112    }
113    return Long.decode(s);
114  }
115
116  public static void main(String[] args) throws Exception {
117
118    // Process command line directives.
119    //
120    for (int i = 0, n = args.length; i < n; ++i) {
121      if (args[i].equals("-da")) {              // image address
122        if (++i == args.length) {
123          System.err.println("Error: The -da flag requires an argument");
124          System.exit(EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
125        }
126        bootImageDataAddress = decodeLong(args[i]);
127        continue;
128      }
129      if (args[i].equals("-ca")) {              // image address
130        if (++i == args.length) {
131          System.err.println("Error: The -ca flag requires an argument");
132          System.exit(EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
133        }
134        bootImageCodeAddress = decodeLong(args[i]);
135        continue;
136      }
137      if (args[i].equals("-ra")) {              // image address
138        if (++i == args.length) {
139          System.err.println("Error: The -ra flag requires an argument");
140          System.exit(EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
141        }
142        bootImageRMapAddress = decodeLong(args[i]);
143        continue;
144      }
145      if (args[i].equals("-out")) {              // output file
146        if (++i == args.length) {
147          System.err.println("Error: The -out flag requires an argument");
148          System.exit(EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
149        }
150        outFileName = args[i];
151        continue;
152      }
153      System.err.println("Error: unrecognized command line argument: " + args[i]);
154      System.exit(EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
155    }
156
157    if (bootImageDataAddress == 0) {
158      System.err.println("Error: Must specify boot image data load address.");
159      System.exit(EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
160    }
161    if (bootImageCodeAddress == 0) {
162      System.err.println("Error: Must specify boot image code load address.");
163      System.exit(EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
164    }
165    if (bootImageRMapAddress == 0) {
166      System.err.println("Error: Must specify boot image ref map load address.");
167      System.exit(EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
168    }
169    if (outFileName == null) {
170      out = System.out;
171    } else {
172      try {
173        // We'll let an unhandled exception throw an I/O error for us.
174        out = new PrintStream(new FileOutputStream(outFileName));
175      } catch (IOException e) {
176        reportTrouble("Caught an exception while opening" + outFileName + " for writing: " + e.toString());
177      }
178    }
179
180    VM.initForTool();
181
182    emitStuff();
183    if (out.checkError()) {
184      reportTrouble("an output error happened");
185    }
186    //    try {
187    out.close();              // exception thrown up.
188    //    } catch (IOException e) {
189    //      reportTrouble("An output error when closing the output: " + e.toString());
190    //    }
191    System.exit(0);
192  }
193
194  private static void reportTrouble(String msg) {
195    System.err.println(
196        "org.jikesrvm.tools.header_gen.GenerateInterfaceDeclarations: While we were creating InterfaceDeclarations.h, there was a problem.");
197    System.err.println(msg);
198    System.err.print("The build system will delete the output file");
199    if (outFileName != null) {
200      System.err.print(' ');
201      System.err.print(outFileName);
202    }
203    System.err.println();
204
205    System.exit(1);
206  }
207
208  private static void emitStuff() {
209    p("/*------ MACHINE GENERATED by ");
210    p("org.jikesrvm.tools.header_gen.GenerateInterfaceDeclarations.java: DO NOT EDIT");
211    p("------*/\n\n");
212
213    if (VM.PortableNativeSync) {
214      pln("#define PORTABLE_NATIVE_SYNC 1");
215      pln();
216    }
217
218    pln("#ifdef NEED_BOOT_RECORD_DECLARATIONS");
219    emitBootRecordDeclarations();
220    pln("#endif /* NEED_BOOT_RECORD_DECLARATIONS */");
221    pln();
222
223    pln("#ifdef NEED_BOOT_RECORD_INITIALIZATION");
224    emitBootRecordInitialization();
225    pln("#endif /* NEED_BOOT_RECORD_INITIALIZATION */");
226    pln();
227
228    pln("#ifdef NEED_VIRTUAL_MACHINE_DECLARATIONS");
229    emitVirtualMachineDeclarations(bootImageDataAddress, bootImageCodeAddress, bootImageRMapAddress);
230    pln("#endif /* NEED_VIRTUAL_MACHINE_DECLARATIONS */");
231    pln();
232
233    pln("#ifdef NEED_EXIT_STATUS_CODES");
234    emitExitStatusCodes();
235    pln("#endif /* NEED_EXIT_STATUS_CODES */");
236    pln();
237
238    pln("#ifdef NEED_ASSEMBLER_DECLARATIONS");
239    emitAssemblerDeclarations();
240    pln("#endif /* NEED_ASSEMBLER_DECLARATIONS */");
241
242    pln("#ifdef NEED_MEMORY_MANAGER_DECLARATIONS");
243    pln("#define MAXHEAPS " + org.jikesrvm.mm.mminterface.MemoryManager.getMaxHeaps());
244    pln("#endif /* NEED_MEMORY_MANAGER_DECLARATIONS */");
245    pln();
246
247  }
248
249  static void emitCDeclarationsForJavaType(String Cname, RVMClass cls) {
250
251    // How many instance fields are there?
252    //
253    RVMField[] allFields = cls.getDeclaredFields();
254    int fieldCount = 0;
255    for (RVMField field : allFields) {
256      if (!field.isStatic()) {
257        fieldCount++;
258      }
259    }
260
261    RVMField[] fields = new RVMField[fieldCount];
262    for (int i = 0, j = 0; i < allFields.length; i++) {
263      if (!allFields[i].isStatic()) {
264        fields[j++] = allFields[i];
265      }
266    }
267    Arrays.sort(fields, new AscendingOffsetComparator());
268
269    // Emit field declarations
270    //
271    p("struct " + Cname + " {\n");
272
273    // Set up cursor - scalars will waste 4 bytes on 64-bit arch
274    //
275    boolean needsAlign = VM.BuildFor64Addr;
276    int addrSize = VM.BuildFor32Addr ? 4 : 8;
277
278    // Header Space for objects
279    int startOffset = ObjectModel.objectStartOffset(cls);
280    Offset current = Offset.fromIntSignExtend(startOffset);
281    for (int i = 0; current.sLT(fields[0].getOffset()); i++) {
282      pln("  uint32_t    headerPadding" + i + ";\n");
283      current = current.plus(4);
284    }
285
286    for (int i = 0; i < fields.length; i++) {
287      RVMField field = fields[i];
288      TypeReference t = field.getType();
289      Offset offset = field.getOffset();
290      String name = field.getName().toString();
291      // Align by blowing 4 bytes if needed
292      if (needsAlign && current.plus(4).EQ(offset)) {
293        pln("  uint32_t    padding" + i + ";");
294        current = current.plus(4);
295      }
296      if (!current.EQ(offset)) {
297        System.err.printf("current (%d) and offset (%d) are neither identical nor differ by 4",
298                          current.toInt(),
299                          offset.toInt());
300        System.exit(1);
301      }
302      if (t.isIntType()) {
303        current = current.plus(4);
304        p("   uint32_t " + name + ";\n");
305      } else if (t.isLongType()) {
306        current = current.plus(8);
307        p("   uint64_t " + name + ";\n");
308      } else if (t.isWordLikeType()) {
309        p("   Address " + name + ";\n");
310        current = current.plus(addrSize);
311      } else if (t.isArrayType() && t.getArrayElementType().isWordLikeType()) {
312        p("   Address * " + name + ";\n");
313        current = current.plus(addrSize);
314      } else if (t.isArrayType() && t.getArrayElementType().isIntType()) {
315        p("   unsigned int * " + name + ";\n");
316        current = current.plus(addrSize);
317      } else if (t.isReferenceType()) {
318        p("   Address " + name + ";\n");
319        current = current.plus(addrSize);
320      } else {
321        System.err.println("Unexpected field " + name + " with type " + t);
322        throw new RuntimeException("unexpected field type");
323      }
324    }
325
326    p("};\n");
327  }
328
329  static void emitBootRecordDeclarations() {
330    RVMClass bootRecord = TypeReference.findOrCreate(org.jikesrvm.runtime.BootRecord.class).resolve().asClass();
331    emitCDeclarationsForJavaType("BootRecord", bootRecord);
332  }
333
334  // Emit declarations for BootRecord object.
335  //
336  static void emitBootRecordInitialization() {
337    RVMClass bootRecord = TypeReference.findOrCreate(org.jikesrvm.runtime.BootRecord.class).resolve().asClass();
338    RVMField[] fields = bootRecord.getDeclaredFields();
339
340    // emit field initializers
341    //
342    p("static void setLinkage(struct BootRecord* br){\n");
343    for (int i = fields.length; --i >= 0;) {
344      RVMField field = fields[i];
345      if (field.isStatic()) {
346        continue;
347      }
348
349      String fieldName = field.getName().toString();
350      if (fieldName.indexOf("gcspy") > -1 && !VM.BuildWithGCSpy) {
351        continue;  // ugh.  NOTE: ugly hack to side-step unconditional inclusion of GCSpy stuff
352      }
353      int suffixIndex = fieldName.indexOf("IP");
354      if (suffixIndex > 0) {
355        // java field "xxxIP" corresponds to C function "xxx"
356        String functionName = fieldName.substring(0, suffixIndex);
357        // e. g.,
358        //sysFOOIP = (int) sysFOO;
359        p("  br->" + fieldName + " = (Address)" + functionName + ";\n");
360      } else if (fieldName.equals("sysJavaVM")) {
361        p("  br->" + fieldName + " = (Address)&" + fieldName + ";\n");
362      }
363    }
364
365    p("}\n");
366  }
367
368  // Emit virtual machine class interface information.
369  //
370  static void emitVirtualMachineDeclarations(long bootImageDataAddress, long bootImageCodeAddress,
371                                             long bootImageRMapAddress) {
372
373    // load address for the boot image
374    //
375    pln("bootImageDataAddress", Address.fromLong(bootImageDataAddress));
376    pln("bootImageCodeAddress", Address.fromLong(bootImageCodeAddress));
377    pln("bootImageRMapAddress", Address.fromLong(bootImageRMapAddress));
378
379    // values in Constants, from Configuration
380    //
381    pln("Constants_STACK_SIZE_GUARD", StackFrameLayout.getStackSizeGuard());
382    pln("Constants_INVISIBLE_METHOD_ID", StackFrameLayout.getInvisibleMethodID());
383    pln("Constants_STACKFRAME_HEADER_SIZE", StackFrameLayout.getStackFrameHeaderSize());
384    pln("Constants_STACKFRAME_METHOD_ID_OFFSET", StackFrameLayout.getStackFrameMethodIDOffset());
385    pln("Constants_STACKFRAME_FRAME_POINTER_OFFSET", StackFrameLayout.getStackFramePointerOffset());
386    pln("Constants_STACKFRAME_SENTINEL_FP", StackFrameLayout.getStackFrameSentinelFP());
387
388    pln("ThinLockConstants_TL_THREAD_ID_SHIFT", TL_THREAD_ID_SHIFT);
389
390    // values in RuntimeEntrypoints
391    //
392    pln("Runtime_TRAP_UNKNOWN", RuntimeEntrypoints.TRAP_UNKNOWN);
393    pln("Runtime_TRAP_NULL_POINTER", RuntimeEntrypoints.TRAP_NULL_POINTER);
394    pln("Runtime_TRAP_ARRAY_BOUNDS", RuntimeEntrypoints.TRAP_ARRAY_BOUNDS);
395    pln("Runtime_TRAP_DIVIDE_BY_ZERO", RuntimeEntrypoints.TRAP_DIVIDE_BY_ZERO);
396    pln("Runtime_TRAP_STACK_OVERFLOW", RuntimeEntrypoints.TRAP_STACK_OVERFLOW);
397    pln("Runtime_TRAP_CHECKCAST", RuntimeEntrypoints.TRAP_CHECKCAST);
398    pln("Runtime_TRAP_REGENERATE", RuntimeEntrypoints.TRAP_REGENERATE);
399    pln("Runtime_TRAP_JNI_STACK", RuntimeEntrypoints.TRAP_JNI_STACK);
400    pln("Runtime_TRAP_MUST_IMPLEMENT", RuntimeEntrypoints.TRAP_MUST_IMPLEMENT);
401    pln("Runtime_TRAP_STORE_CHECK", RuntimeEntrypoints.TRAP_STORE_CHECK);
402    pln("Runtime_TRAP_UNREACHABLE_BYTECODE", RuntimeEntrypoints.TRAP_UNREACHABLE_BYTECODE);
403    pln();
404
405    // fields in RVMThread
406    //
407    Offset offset = Entrypoints.threadStackField.getOffset();
408    pln("RVMThread_stack_offset", offset);
409    offset = Entrypoints.stackLimitField.getOffset();
410    pln("RVMThread_stackLimit_offset", offset);
411    offset = Entrypoints.threadExceptionRegistersField.getOffset();
412    pln("RVMThread_exceptionRegisters_offset", offset);
413    offset = Entrypoints.jniEnvField.getOffset();
414    pln("RVMThread_jniEnv_offset", offset);
415    offset = Entrypoints.execStatusField.getOffset();
416    pln("RVMThread_execStatus_offset", offset);
417    // constants in RVMThread
418    pln("RVMThread_TERMINATED",  RVMThread.TERMINATED);
419    // fields in Registers
420    //
421    offset = ArchEntrypoints.registersGPRsField.getOffset();
422    pln("Registers_gprs_offset", offset);
423    offset = ArchEntrypoints.registersFPRsField.getOffset();
424    pln("Registers_fprs_offset", offset);
425    offset = ArchEntrypoints.registersIPField.getOffset();
426    pln("Registers_ip_offset", offset);
427
428    offset = ArchEntrypoints.registersInUseField.getOffset();
429    pln("Registers_inuse_offset", offset);
430
431    // fields in JNIEnvironment
432    offset = Entrypoints.JNIExternalFunctionsField.getOffset();
433    pln("JNIEnvironment_JNIExternalFunctions_offset", offset);
434
435    arch.emitArchVirtualMachineDeclarations();
436  }
437
438  // Codes for exit(3).
439  static void emitExitStatusCodes() {
440    pln("/* Automatically generated from the exitStatus declarations in ExitStatus.java */");
441    pln("EXIT_STATUS_EXECUTABLE_NOT_FOUND", EXIT_STATUS_EXECUTABLE_NOT_FOUND);
442    pln("EXIT_STATUS_COULD_NOT_EXECUTE", EXIT_STATUS_COULD_NOT_EXECUTE);
443    pln("EXIT_STATUS_MISC_TROUBLE", EXIT_STATUS_MISC_TROUBLE);
444    pln("EXIT_STATUS_IMPOSSIBLE_LIBRARY_FUNCTION_ERROR", EXIT_STATUS_IMPOSSIBLE_LIBRARY_FUNCTION_ERROR);
445    pln("EXIT_STATUS_SYSCALL_TROUBLE", EXIT_STATUS_SYSCALL_TROUBLE);
446    pln("EXIT_STATUS_TIMER_TROUBLE", EXIT_STATUS_TIMER_TROUBLE);
447    pln("EXIT_STATUS_UNSUPPORTED_INTERNAL_OP", EXIT_STATUS_UNSUPPORTED_INTERNAL_OP);
448    pln("EXIT_STATUS_UNEXPECTED_CALL_TO_SYS", EXIT_STATUS_UNEXPECTED_CALL_TO_SYS);
449    pln("EXIT_STATUS_DYING_WITH_UNCAUGHT_EXCEPTION", EXIT_STATUS_DYING_WITH_UNCAUGHT_EXCEPTION);
450    pln("EXIT_STATUS_BOGUS_COMMAND_LINE_ARG", EXIT_STATUS_BOGUS_COMMAND_LINE_ARG);
451    pln("EXIT_STATUS_JNI_TROUBLE", EXIT_STATUS_JNI_TROUBLE);
452    pln("EXIT_STATUS_BAD_WORKING_DIR", EXIT_STATUS_BAD_WORKING_DIR);
453  }
454
455  // Emit assembler constants.
456  //
457  static void emitAssemblerDeclarations() {
458    arch.emitArchAssemblerDeclarations();
459  }
460}
461
462
463