Skip to content
Permalink
Browse files
Add initial llvm framework.
This currently mostly just includes the LLVM installer, but also
includes a first pass at actually compiling a simple mscript program.
The biggest issue right now is actually just figuring out the llvm ir
necessary, so I've short circuited this right now, making it currently
unusable, but neither the wrapper IR nor the exit() function itself are
properly implemented yet anyways.
  • Loading branch information
LadyCailin committed Apr 24, 2020
1 parent dd8b525 commit 2064220a6205c6f12585bdb5a187ccbb28a48a1a
Show file tree
Hide file tree
Showing 18 changed files with 748 additions and 39 deletions.
@@ -354,7 +354,7 @@
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<type>jar</type>
<version>1.13</version>
<version>1.11</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
@@ -6,6 +6,7 @@
import com.laytonsmith.PureUtilities.Common.ArrayUtils;
import com.laytonsmith.PureUtilities.Common.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -22,13 +23,15 @@
public class ArgumentSuite {

private final Map<String, ArgumentParser> suite;
private final Map<String, Boolean> undocumentedSuites;
private final Map<String, String> aliases;
private String description;

public ArgumentSuite() {
suite = new TreeMap<>((o1, o2) -> {
return o1.compareTo(o2);
});
undocumentedSuites = new HashMap<>();
aliases = new LinkedHashMap<>();
}

@@ -38,12 +41,15 @@ public ArgumentSuite() {
*
* @param modeName The name of this mode. This may not contain spaces.
* @param mode The sub-ArgumentParser that will be used when in this mode.
* @param undocumented Undocumented tools do not show up in the built description, though otherwise work and
* can be retrieved as usual through the getMode methods.
* @return
* @throws IllegalArgumentException if the name of the mode contains spaces
*/
public ArgumentSuite addMode(String modeName, ArgumentParser mode) {
public ArgumentSuite addMode(String modeName, ArgumentParser mode, boolean undocumented) {
validateModeName(modeName);
suite.put(modeName, mode);
undocumentedSuites.put(modeName, undocumented);
return this;
}

@@ -156,6 +162,11 @@ public String getBuiltDescription() {
}
b.append("Modes: (a mode must be the first argument) \n");
for(String mode : suite.keySet()) {
if(undocumentedSuites.get(mode) == true) {
// Skip undocumented values.
continue;
}

b.append("\t").append(TermColors.BOLD).append(TermColors.BRIGHT_GREEN).append(mode);
if(aliases.containsValue(mode)) {
List<String> keys = new ArrayList<>();
@@ -13,6 +13,20 @@
*/
public class OSUtils {

/**
* The bit depth of the OS.
*/
public static enum BitDepth {
/**
* The OS is a 32 bit architecture.
*/
B32,
/**
* The OS is a 64 bit architecture.
*/
B64
}

public static enum OS {
WINDOWS,
MAC,
@@ -190,6 +204,21 @@ public static List<ProcessInfo> GetRunningProcesses() {
}
}

public static BitDepth GetOSBitDepth() {
if(GetOS().isWindows()) {
// Windows lies if we're running 32 bit java on a 64 bit architecture, but this really isn't
// what we need to know, so "thanks".
String arch = System.getenv("PROCESSOR_ARCHITECTURE");
String wow64Arch = System.getenv("PROCESSOR_ARCHITEW6432");

return arch != null && arch.endsWith("64")
|| wow64Arch != null && wow64Arch.endsWith("64")
? BitDepth.B64 : BitDepth.B64;
} else {
return System.getProperty("os.arch").endsWith("64") ? BitDepth.B64 : BitDepth.B32;
}
}

private static List<ProcessInfo> GetRunningProcessesWindows() throws InterruptedException, IOException {
List<ProcessInfo> list = new ArrayList<>();
String cmd = CommandExecutor.Execute("tasklist.exe /fo list");
@@ -1,6 +1,7 @@
package com.laytonsmith.annotations;

import com.laytonsmith.core.PlatformResolver;
import com.laytonsmith.core.asm.LLVMPlatformResolver;
import com.laytonsmith.core.environments.Environment;
import com.laytonsmith.core.functions.bash.BashPlatformResolver;
import java.lang.annotation.ElementType;
@@ -22,7 +23,8 @@

public enum Platforms {
INTERPRETER_JAVA(null, "Java Interpreter"),
COMPILER_BASH(new BashPlatformResolver(), "Bash Compiler");
COMPILER_BASH(new BashPlatformResolver(), "Bash Compiler"),
COMPILER_LLVM(new LLVMPlatformResolver(), "LLVM Compiler");
private final PlatformResolver resolver;
private final String platformName;

@@ -127,7 +127,7 @@ public static CmdlineToolCollection GetCommandLineTools() {
CommandLineTool tool = ctool.newInstance();
ArgumentParser ap = tool.getArgumentParser();
String toolName = ctool.getAnnotation(tool.class).value();
suite.addMode(toolName, ap);
suite.addMode(toolName, ap, ctool.getAnnotation(tool.class).undocumented());
String[] aliases = ctool.getAnnotation(tool.class).aliases();
if(aliases != null) {
for(String alias : aliases) {
@@ -1127,10 +1127,8 @@ public static class CmdlineMode extends AbstractCommandLineTool {
@Override
public ArgumentParser getArgumentParser() {
return ArgumentParser.GetParser()
.addDescription("Given a source file, runs it in cmdline mode. This is similar to"
+ " the interpreter mode, but allows for tty input (which is required for some functions,"
+ " like the prompt_* functions) and provides better information for errors, as the"
+ " file is known.")
.addDescription("Given a source file, runs it in cmdline mode. This is the \"main\" way"
+ " of executing source files.")
.addArgument(new ArgumentBuilder()
.setDescription("File path/arguments")
.setUsageName("file and args")
@@ -1572,7 +1570,8 @@ public void execute(ArgumentParser.ArgumentParserResults parsedArgs) throws Exce
modeForHelp = getSuite().getModeFromAlias(modeForHelp);
if(modeForHelp == null) {
//Display the general help
StreamUtils.GetSystemOut().println(getSuite().getBuiltDescription());
ArgumentSuite suite = getSuite();
StreamUtils.GetSystemOut().println(suite.getBuiltDescription());
System.exit(0);
} else {
//Display the help for this mode
@@ -1,5 +1,6 @@
package com.laytonsmith.core;

import com.laytonsmith.core.environments.Environment;
import com.laytonsmith.core.natives.interfaces.Mixed;

/**
@@ -8,5 +9,5 @@
*/
public interface PlatformResolver {

public String outputConstant(Mixed c);
public String outputConstant(Mixed c, Environment env);
}
@@ -0,0 +1,138 @@
package com.laytonsmith.core.asm;

import com.laytonsmith.PureUtilities.CommandExecutor;
import com.laytonsmith.PureUtilities.Common.FileUtil;
import com.laytonsmith.PureUtilities.Common.OSUtils;
import com.laytonsmith.annotations.api;
import com.laytonsmith.core.InternalException;
import com.laytonsmith.core.MethodScriptCompiler;
import com.laytonsmith.core.ParseTree;
import com.laytonsmith.core.Profiles;
import com.laytonsmith.core.Static;
import com.laytonsmith.core.compiler.CompilerEnvironment;
import com.laytonsmith.core.constructs.CFunction;
import com.laytonsmith.core.environments.Environment;
import com.laytonsmith.core.environments.GlobalEnv;
import com.laytonsmith.core.exceptions.ConfigCompileException;
import com.laytonsmith.core.exceptions.ConfigCompileGroupException;
import com.laytonsmith.core.functions.FunctionBase;
import com.laytonsmith.core.functions.FunctionList;
import com.laytonsmith.persistence.DataSourceException;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
*
*/
public class AsmCompiler {

private final static Set<Class<? extends Environment.EnvironmentImpl>> ENVS = new HashSet<>();
static {
ENVS.add(GlobalEnv.class);
ENVS.add(CompilerEnvironment.class);
ENVS.add(LLVMEnvironment.class);
}

private final File llc;
private final File lld;

public AsmCompiler(File llc, File lld) {
this.llc = llc;
this.lld = lld;
}

/**
* Looks in platform specific default locations for the assembler and linker.
*/
public AsmCompiler() {
File llc;
File lld;
if(OSUtils.GetOS().isWindows()) {
llc = new File("C:\\Program Files\\LLVM\\bin\\llc.exe");
lld = new File("C:\\Program Files\\LLVM\\bin\\lld-link.exe");
} else {
throw new UnsupportedOperationException("OS not yet supported");
}
this.llc = llc;
this.lld = lld;
}

public void compileEntryPoint(File file, File outputDirectory, String exeName)
throws IOException, DataSourceException, URISyntaxException, Profiles.InvalidProfileException,
ConfigCompileException, ConfigCompileGroupException, InterruptedException {
StringBuilder ir = new StringBuilder();
StringBuilder strings = new StringBuilder();

strings.append("\n");
String irTop = "@exit = global i64 0\n"
+ "declare i32 @puts(i8* nocapture) nounwind\n"
+ "define i64* @main() {\n";
String irBottom = "exit:\n"
+ "ret i64* @exit\n"
+ "}\n"
+ "define i32* @mainCRTStartup() {\n"
+ "%ret = call i64* @main()\n"
+ "%ret32 = bitcast i64* @exit to i32*\n"
+ "ret i32* %ret32\n"
+ "}";
ir.append(strings.toString());
ir.append(irTop);
compileFile(file, ir);
ir.append(irBottom);
File ll = new File(outputDirectory, exeName + ".ll");
File obj = new File(outputDirectory, exeName + ".obj");
FileUtil.write(ir.toString(), ll);
{
String[] args = new String[]{
llc.getAbsolutePath(),
"--filetype=obj",
"-o=\"" + obj.getAbsolutePath() + "\"",
ll.getAbsolutePath()
};
CommandExecutor ex = new CommandExecutor(args);
// StringWrit
// ex.setSystemOut();
String error = "Not yet implemented!"; //CommandExecutor.Execute(outputDirectory, );
if(!"".equals(error)) {
throw new InternalException("Assembly failed:\n" + error);
}
}
{
String error = CommandExecutor.Execute(outputDirectory, lld.getAbsolutePath(), obj.getAbsolutePath());
if(!"".equals(error)) {
throw new InternalException("Linking failed:\n" + error);
}
}
}

private void compileFile(File file, StringBuilder ir)
throws IOException, DataSourceException, Profiles.InvalidProfileException,
ConfigCompileException, URISyntaxException, ConfigCompileGroupException {
String script = FileUtil.read(file);
Environment env = Static.GenerateStandaloneEnvironment(true);
env = env.cloneAndAdd(new LLVMEnvironment());
ParseTree tree
= MethodScriptCompiler.compile(MethodScriptCompiler.lex(script, env, file, true), env, ENVS);
ir.append(getIR(tree.getChildAt(0), env));
}

private String getIR(ParseTree node, Environment env) throws ConfigCompileException {
if(!(node.getData() instanceof CFunction)) {
return api.Platforms.COMPILER_LLVM.getResolver().outputConstant(node.getData(), env);
}
CFunction cf = ((CFunction) node.getData());
FunctionBase fb = FunctionList.getFunction(cf, api.Platforms.COMPILER_LLVM, ENVS);
if(fb instanceof LLVMFunction) {
return ((LLVMFunction) fb).getIR(node.getTarget(), env, null, node.getChildren().toArray(new ParseTree[0]));
} else {
throw new ConfigCompileException("Unsupported function " + cf.getName(), node.getTarget());
}
}
}

0 comments on commit 2064220

Please sign in to comment.