/** * Ensures that LLVM code exists for all dependencies of a main-method- containing class/file.This * involves either finding an existing .ll file (which has been updated more recently than the * corresponding source file or building a new one * * @throws CompileException */ private static void generateLLVM( List<String> linkCommand, Set<String> generics, Set<String> arrays) throws IOException, ShadowException, ParseException, ConfigurationException, TypeCheckException, CompileException { Type.clearTypes(); Path mainFile = currentJob.getMainFile(); String mainFileName = stripExt(mainFile.toString()); List<Node> nodes; try { nodes = TypeChecker.typeCheck(mainFile.toFile(), currentJob.isForceRecompile()); } catch (TypeCheckException e) { logger.error(mainFile + " FAILED TO TYPE CHECK"); throw e; } if (!currentJob.isCheckOnly()) { for (Node node : nodes) { File file = node.getFile(); String name = stripExt(file.getName()); String path = stripExt(file.getCanonicalPath()); File llvmFile = new File(path + ".ll"); Type type = node.getType(); // set data for main class if (path.equals(mainFileName)) { mainClass = type.toString(Type.MANGLE); SequenceType arguments = new SequenceType(new ArrayType(Type.STRING)); if (type.getMatchingMethod("main", arguments) != null) mainArguments = true; else if (type.getMatchingMethod("main", new SequenceType()) != null) mainArguments = false; else throw new ShadowException( "File " + file.getPath() + " does not contain an appropriate main() method"); } // if the LLVM didn't exist, the full .shadow file would have been used if (file.getPath().endsWith(".meta")) { logger.info("Using pre-existing LLVM code for " + name); addToLink(node.getType(), file, linkCommand); LLVMOutput.readGenericAndArrayClasses(llvmFile, generics, arrays); } else { logger.info("Generating LLVM code for " + name); // gets top level class TACModule module = new TACBuilder().build(node); logger.debug(module.toString()); // Write to file String className = typeToFileName(type); llvmFile = new File(file.getParentFile(), className + ".ll"); File nativeFile = new File(file.getParentFile(), className + ".native.ll"); LLVMOutput output = new LLVMOutput(llvmFile); try { output.build(module); } catch (ShadowException e) { logger.error(file + " FAILED TO COMPILE"); output.close(); if (llvmFile.exists()) llvmFile.delete(); throw new CompileException(e.getMessage()); } if (llvmFile.exists()) linkCommand.add(llvmFile.getCanonicalPath()); else throw new ShadowException("Failed to generate " + llvmFile.getPath()); if (nativeFile.exists()) linkCommand.add(nativeFile.getCanonicalPath()); // it's important to add generics after generating the LLVM, since more are made generics.addAll(output.getGenericClasses()); arrays.addAll(output.getArrayClasses()); } } } }
public static void run(String[] args) throws FileNotFoundException, ParseException, ShadowException, IOException, org.apache.commons.cli.ParseException, ConfigurationException, TypeCheckException, CompileException { // Detect and establish the current settings and arguments Arguments compilerArgs = new Arguments(args); // Exit if help was requested (Arguments handles printing) if (compilerArgs.hasOption(Arguments.HELP)) return; // Exit if information was requested (Arguments handles printing) if (compilerArgs.hasOption(Arguments.INFORMATION)) return; // Detect and establish the current settings based on the arguments config = Configuration.buildConfiguration( compilerArgs.getMainFileArg(), compilerArgs.getConfigFileArg(), false); currentJob = new Job(compilerArgs); Path system = config.getSystemImport(); Path unwindFile = Paths.get("shadow", "Unwind" + config.getArch() + ".ll"); unwindFile = system.resolve(unwindFile); // Locate the file defining platform-specific system calls Path OsFile = Paths.get("shadow" + File.separator + config.getOs() + ".ll"); OsFile = system.resolve(OsFile); List<String> linkCommand = new ArrayList<String>(); linkCommand.add("llvm-link"); linkCommand.add("-"); linkCommand.add(unwindFile.toString()); linkCommand.add(OsFile.toString()); // Begin the checking/compilation process long startTime = System.currentTimeMillis(); Set<String> generics = new HashSet<String>(); Set<String> arrays = new HashSet<String>(); generateLLVM(linkCommand, generics, arrays); if (!currentJob.isCheckOnly() && !currentJob.isNoLink()) { // any output after this point is important, avoid getting it mixed in with previous output System.out.println(); System.out.flush(); try { Thread.sleep(500); } catch (InterruptedException ex) { } logger.info("Building for target \"" + config.getTarget() + "\""); List<String> assembleCommand = config.getLinkCommand(currentJob); Path mainLL; if (mainArguments) mainLL = Paths.get("shadow", "Main.ll"); else mainLL = Paths.get("shadow", "NoArguments.ll"); mainLL = system.resolve(mainLL); BufferedReader main = Files.newBufferedReader(mainLL, UTF8); String endian = "e"; // little Endian String pointerAlignment = "p:" + config.getArch() + ":" + config.getArch() + ":" + config.getArch(); String dataAlignment = "i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f16:16:16-f32:32:32-f64:64:64"; String aggregateAlignment = "a:0:" + config.getArch(); String nativeIntegers = "n8:16:32:64"; String dataLayout = "-default-data-layout=" + endian + "-" + pointerAlignment + "-" + dataAlignment + "-" + aggregateAlignment + "-" + nativeIntegers; Process link = new ProcessBuilder(linkCommand).redirectError(Redirect.INHERIT).start(); Process optimize = new ProcessBuilder("opt", "-mtriple", config.getTarget(), "-O3", dataLayout) .redirectError(Redirect.INHERIT) .start(); Process compile = new ProcessBuilder( "llc", "-mtriple", config.getTarget(), "-O3") /*.redirectOutput(new File("a.s"))*/ .redirectError(Redirect.INHERIT) .start(); Process assemble = new ProcessBuilder(assembleCommand) .redirectOutput(Redirect.INHERIT) .redirectError(Redirect.INHERIT) .start(); try { new Pipe(link.getInputStream(), optimize.getOutputStream()).start(); new Pipe(optimize.getInputStream(), compile.getOutputStream()).start(); new Pipe(compile.getInputStream(), assemble.getOutputStream()).start(); String line = main.readLine(); final OutputStream out = link.getOutputStream(); while (line != null) { if (line.contains("@main")) { // declare externally defined generics for (String generic : generics) out.write(LLVMOutput.declareGeneric(generic).getBytes()); for (String array : arrays) out.write(LLVMOutput.declareArray(array).getBytes()); out.write(System.lineSeparator().getBytes()); } else if (line.trim().startsWith("%genericSet")) line = line.replace("%genericSize", "" + generics.size() * 2); else if (line.trim().startsWith("%arraySet")) line = line.replace("%arraySize", "" + arrays.size() * 2); else if (line.trim().startsWith("invoke")) { // add in all externally declared generics LLVMOutput.addGenerics("%genericSet", generics, false, out); LLVMOutput.addGenerics("%arraySet", arrays, true, out); } line = line.replace("shadow.test..Test", mainClass) + System.lineSeparator(); out.write(line.getBytes()); line = main.readLine(); } try { main.close(); } catch (IOException ex) { } try { link.getOutputStream().flush(); } catch (IOException ex) { } try { link.getOutputStream().close(); } catch (IOException ex) { } if (link.waitFor() != 0) throw new CompileException("FAILED TO LINK"); if (optimize.waitFor() != 0) throw new CompileException("FAILED TO OPTIMIZE"); if (compile.waitFor() != 0) throw new CompileException("FAILED TO COMPILE"); if (assemble.waitFor() != 0) throw new CompileException("FAILED TO ASSEMBLE"); } catch (InterruptedException ex) { } finally { link.destroy(); optimize.destroy(); compile.destroy(); assemble.destroy(); } logger.info("SUCCESS: Built in " + (System.currentTimeMillis() - startTime) + "ms"); } }