org.qbicc.main.Main Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of qbicc-main Show documentation
Show all versions of qbicc-main Show documentation
The main entry point for Qbicc
package org.qbicc.main;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer;
import java.util.jar.JarInputStream;
import io.smallrye.common.constraint.Assert;
import org.apache.maven.settings.Settings;
import org.apache.maven.settings.building.SettingsBuildingException;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.jboss.logmanager.Level;
import org.jboss.logmanager.LogManager;
import org.jboss.logmanager.Logger;
import org.qbicc.context.CompilationContext;
import org.qbicc.context.Diagnostic;
import org.qbicc.context.DiagnosticContext;
import org.qbicc.driver.BaseDiagnosticContext;
import org.qbicc.driver.BuilderStage;
import org.qbicc.driver.ClassPathItem;
import org.qbicc.driver.Driver;
import org.qbicc.driver.ElementBodyCopier;
import org.qbicc.driver.ElementBodyCreator;
import org.qbicc.driver.ElementInitializer;
import org.qbicc.driver.ElementVisitorAdapter;
import org.qbicc.driver.GraphGenConfig;
import org.qbicc.driver.Phase;
import org.qbicc.driver.plugin.DriverPlugin;
import org.qbicc.interpreter.Vm;
import org.qbicc.interpreter.VmThread;
import org.qbicc.interpreter.impl.VmImpl;
import org.qbicc.machine.arch.Cpu;
import org.qbicc.machine.arch.Platform;
import org.qbicc.machine.object.ObjectFileProvider;
import org.qbicc.machine.probe.CProbe;
import org.qbicc.machine.tool.CToolChain;
import org.qbicc.machine.vfs.AbsoluteVirtualPath;
import org.qbicc.machine.vfs.VFSUtils;
import org.qbicc.machine.vfs.VirtualFileSystem;
import org.qbicc.plugin.apploader.InitAppClassLoaderHook;
import org.qbicc.plugin.constants.ConstantBasicBlockBuilder;
import org.qbicc.plugin.conversion.NumericalConversionBasicBlockBuilder;
import org.qbicc.plugin.core.CoreAnnotationTypeBuilder;
import org.qbicc.plugin.coreclasses.ArrayLengthBasicBlockBuilder;
import org.qbicc.plugin.coreclasses.BasicHeaderManualInitializer;
import org.qbicc.plugin.coreclasses.CoreClasses;
import org.qbicc.plugin.correctness.BuildTimeOnlyElementHandler;
import org.qbicc.plugin.correctness.RuntimeChecksBasicBlockBuilder;
import org.qbicc.plugin.correctness.StaticChecksBasicBlockBuilder;
import org.qbicc.plugin.dispatch.DevirtualizingBasicBlockBuilder;
import org.qbicc.plugin.dispatch.DispatchTableBuilder;
import org.qbicc.plugin.dispatch.DispatchTableEmitter;
import org.qbicc.plugin.dot.DotGenerator;
import org.qbicc.plugin.gc.common.GcCommon;
import org.qbicc.plugin.gc.common.MultiNewArrayExpansionBasicBlockBuilder;
import org.qbicc.plugin.gc.nogc.NoGcBasicBlockBuilder;
import org.qbicc.plugin.gc.nogc.NoGcSetupHook;
import org.qbicc.plugin.gc.nogc.NoGcTypeSystemConfigurator;
import org.qbicc.plugin.instanceofcheckcast.InstanceOfCheckCastBasicBlockBuilder;
import org.qbicc.plugin.instanceofcheckcast.SupersDisplayBuilder;
import org.qbicc.plugin.instanceofcheckcast.SupersDisplayEmitter;
import org.qbicc.plugin.intrinsics.IntrinsicBasicBlockBuilder;
import org.qbicc.plugin.intrinsics.core.CoreIntrinsics;
import org.qbicc.plugin.layout.ObjectAccessLoweringBuilder;
import org.qbicc.plugin.linker.EmscriptenLinkStage;
import org.qbicc.plugin.linker.LinkStage;
import org.qbicc.plugin.llvm.LLVMCompatibleBasicBlockBuilder;
import org.qbicc.plugin.llvm.LLVMCompileStage;
import org.qbicc.plugin.llvm.LLVMCompiler;
import org.qbicc.plugin.llvm.LLVMCompilerImpl;
import org.qbicc.plugin.llvm.LLVMDefaultModuleCompileStage;
import org.qbicc.plugin.llvm.LLVMEmscriptenCompiler;
import org.qbicc.plugin.llvm.LLVMGenerator;
import org.qbicc.plugin.llvm.LLVMIntrinsics;
import org.qbicc.plugin.llvm.LLVMReferencePointerFactory;
import org.qbicc.plugin.llvm.LLVMStripStackMapStage;
import org.qbicc.plugin.lowering.AbortingThrowLoweringBasicBlockBuilder;
import org.qbicc.plugin.lowering.BooleanAccessCopier;
import org.qbicc.plugin.lowering.FunctionLoweringElementHandler;
import org.qbicc.plugin.lowering.InitCheckLoweringBasicBlockBuilder;
import org.qbicc.plugin.lowering.InvocationLoweringBasicBlockBuilder;
import org.qbicc.plugin.lowering.LocalVariableFindingBasicBlockBuilder;
import org.qbicc.plugin.lowering.LocalVariableLoweringBasicBlockBuilder;
import org.qbicc.plugin.lowering.MemberPointerCopier;
import org.qbicc.plugin.lowering.StaticFieldLoweringBasicBlockBuilder;
import org.qbicc.plugin.lowering.ThrowExceptionHelper;
import org.qbicc.plugin.lowering.ThrowLoweringBasicBlockBuilder;
import org.qbicc.plugin.lowering.VMHelpersSetupHook;
import org.qbicc.plugin.main_method.AddMainClassHook;
import org.qbicc.plugin.main_method.MainMethod;
import org.qbicc.plugin.methodinfo.MethodDataEmitter;
import org.qbicc.plugin.native_.ConstTypeResolver;
import org.qbicc.plugin.native_.ConstantDefiningBasicBlockBuilder;
import org.qbicc.plugin.native_.ExternExportTypeBuilder;
import org.qbicc.plugin.native_.FunctionTypeResolver;
import org.qbicc.plugin.native_.InternalNativeTypeResolver;
import org.qbicc.plugin.native_.NativeBasicBlockBuilder;
import org.qbicc.plugin.native_.NativeBindingMethodConfigurator;
import org.qbicc.plugin.native_.NativeTypeBuilder;
import org.qbicc.plugin.native_.NativeTypeResolver;
import org.qbicc.plugin.native_.NativeXtorLoweringHook;
import org.qbicc.plugin.native_.PointerBasicBlockBuilder;
import org.qbicc.plugin.native_.PointerTypeResolver;
import org.qbicc.plugin.native_.StructMemberAccessBasicBlockBuilder;
import org.qbicc.plugin.nativeimage.FeatureProcessor;
import org.qbicc.plugin.objectmonitor.ObjectMonitorBasicBlockBuilder;
import org.qbicc.plugin.opt.FinalFieldLoadOptimizer;
import org.qbicc.plugin.opt.GotoRemovingVisitor;
import org.qbicc.plugin.opt.InliningBasicBlockBuilder;
import org.qbicc.plugin.opt.LocalMemoryTrackingBasicBlockBuilder;
import org.qbicc.plugin.opt.PhiOptimizerVisitor;
import org.qbicc.plugin.opt.SimpleOptBasicBlockBuilder;
import org.qbicc.plugin.opt.ea.EscapeAnalysisDotGenerator;
import org.qbicc.plugin.opt.ea.EscapeAnalysisDotVisitor;
import org.qbicc.plugin.opt.ea.EscapeAnalysisInterMethodAnalysis;
import org.qbicc.plugin.opt.ea.EscapeAnalysisIntraMethodAnalysis;
import org.qbicc.plugin.opt.ea.EscapeAnalysisOptimizeVisitor;
import org.qbicc.plugin.patcher.AccessorBasicBlockBuilder;
import org.qbicc.plugin.patcher.AccessorTypeBuilder;
import org.qbicc.plugin.patcher.Patcher;
import org.qbicc.plugin.patcher.PatcherResolverBasicBlockBuilder;
import org.qbicc.plugin.patcher.PatcherTypeResolver;
import org.qbicc.plugin.reachability.ReachabilityAnnotationTypeBuilder;
import org.qbicc.plugin.reachability.ReachabilityBlockBuilder;
import org.qbicc.plugin.reachability.ReachabilityInfo;
import org.qbicc.plugin.reachability.ReachabilityRoots;
import org.qbicc.plugin.reflection.Reflection;
import org.qbicc.plugin.reflection.ReflectionIntrinsics;
import org.qbicc.plugin.reflection.ReflectiveMethodAccessorGenerator;
import org.qbicc.plugin.reflection.VarHandleResolvingBasicBlockBuilder;
import org.qbicc.plugin.serialization.BuildtimeHeap;
import org.qbicc.plugin.serialization.ClassObjectSerializer;
import org.qbicc.plugin.serialization.MethodDataStringsSerializer;
import org.qbicc.plugin.serialization.ObjectLiteralSerializingVisitor;
import org.qbicc.plugin.serialization.StringInternTableEmitter;
import org.qbicc.plugin.threadlocal.ThreadLocalBasicBlockBuilder;
import org.qbicc.plugin.threadlocal.ThreadLocalTypeBuilder;
import org.qbicc.plugin.trycatch.LocalThrowHandlingBasicBlockBuilder;
import org.qbicc.plugin.trycatch.SynchronizedMethodBasicBlockBuilder;
import org.qbicc.plugin.verification.ClassInitializingBasicBlockBuilder;
import org.qbicc.plugin.verification.ClassLoadingBasicBlockBuilder;
import org.qbicc.plugin.verification.LowerVerificationBasicBlockBuilder;
import org.qbicc.plugin.verification.MemberResolvingBasicBlockBuilder;
import org.qbicc.plugin.vfs.VFS;
import org.qbicc.plugin.vio.VIO;
import org.qbicc.tool.llvm.LlvmToolChain;
import org.qbicc.type.TypeSystem;
import picocli.CommandLine;
import picocli.CommandLine.ParameterException;
import picocli.CommandLine.ParseResult;
/**
* The main entry point, which can be constructed using a builder or directly invoked.
*/
public class Main implements Callable {
private final List bootPaths;
private final List appPaths;
private final ClassLoader hostAppClassLoader;
private final Path outputPath;
private final String outputName;
private final Consumer> diagnosticsHandler;
private final String mainClass;
private final String gc;
private final boolean isPie;
private final GraphGenConfig graphGenConfig;
private final boolean compileOutput;
private final boolean optMemoryTracking;
private final boolean optPhis;
private final boolean optGotos;
private final boolean optInlining;
private final boolean optEscapeAnalysis;
private final Platform platform;
private final boolean isWasm;
private final boolean smallTypeIds;
private final List librarySearchPaths;
private final List buildFeatures;
private final ClassPathResolver classPathResolver;
private final Backend backend;
private final List optOptions;
private final List llcOptions;
Main(Builder builder) {
outputPath = builder.outputPath;
outputName = builder.outputName;
diagnosticsHandler = builder.diagnosticsHandler;
// todo: this becomes optional
mainClass = Assert.checkNotEmptyParam("builder.mainClass", builder.mainClass);
gc = builder.gc;
isPie = builder.isPie;
graphGenConfig = builder.graphGenConfig;
compileOutput = builder.compileOutput;
optMemoryTracking = builder.optMemoryTracking;
optInlining = builder.optInlining;
optPhis = builder.optPhis;
optGotos = builder.optGotos;
optEscapeAnalysis = builder.optEscapeAnalysis;
platform = builder.platform;
isWasm = platform.getCpu() == Cpu.WASM32;
smallTypeIds = builder.smallTypeIds;
backend = builder.backend;
ArrayList bootPaths = new ArrayList<>(builder.bootPathsPrepend.size() + 6 + builder.bootPathsAppend.size());
bootPaths.addAll(builder.bootPathsPrepend);
optOptions = builder.optOptions;
llcOptions = builder.llcOptions;
// add core things
bootPaths.add(getCoreComponent("qbicc-runtime-api"));
bootPaths.add(getCoreComponent("qbicc-runtime-linux"));
bootPaths.add(getCoreComponent("qbicc-runtime-llvm"));
bootPaths.add(getCoreComponent("qbicc-runtime-main"));
bootPaths.add(getCoreComponent("qbicc-runtime-posix"));
bootPaths.add(getCoreComponent("qbicc-runtime-unwind"));
bootPaths.add(getCoreComponent("qbicc-runtime-zlib"));
boolean nogc = gc.equals("none");
if (nogc) {
bootPaths.add(getCoreComponent("qbicc-runtime-gc-nogc"));
}
bootPaths.add(ClassPathEntry.ofClassLibraries(builder.classLibVersion));
bootPaths.addAll(builder.bootPathsAppend);
this.bootPaths = bootPaths;
appPaths = List.copyOf(builder.appPaths);
librarySearchPaths = builder.librarySearchPaths;
buildFeatures = builder.buildFeatures;
classPathResolver = builder.classPathResolver == null ? this::resolveClassPath : builder.classPathResolver;
List urls = new ArrayList<>();
for (ClassPathEntry cpe : appPaths) {
if (cpe instanceof ClassPathEntry.FilePath fp) {
try {
urls.add(fp.getPath().toUri().toURL());
} catch (MalformedURLException e) {
// Ignore; if it matters will show up CNFE when the ClassLoader is used
}
}
}
hostAppClassLoader = URLClassLoader.newInstance(urls.toArray(new URL[0]));
}
public DiagnosticContext call() {
BaseDiagnosticContext ctxt = new BaseDiagnosticContext();
try {
call0(ctxt);
} catch (Throwable t) {
t.printStackTrace(System.err);
ctxt.error(t, "Compilation failed due to an exception");
}
diagnosticsHandler.accept(ctxt.getDiagnostics());
return ctxt;
}
void call0(BaseDiagnosticContext initialContext) {
final Driver.Builder builder = Driver.builder();
builder.setInitialContext(initialContext);
boolean nogc = gc.equals("none");
boolean llvm = backend.equals(Backend.llvm);
int errors = initialContext.errors();
if (errors == 0) {
builder.setOutputDirectory(outputPath);
// process the class paths
try {
classPathResolver.resolveClassPath(initialContext, builder::addBootClassPathItem, bootPaths);
} catch (IOException e) {
// todo: close class path items?
return;
}
try {
classPathResolver.resolveClassPath(initialContext, builder::addAppClassPathItem, appPaths);
} catch (IOException e) {
return;
}
// first, probe the target platform
Platform target = platform;
builder.setTargetPlatform(target);
Optional optionalProvider = ObjectFileProvider.findProvider(target.getObjectType(), Main.class.getClassLoader());
if (optionalProvider.isEmpty()) {
initialContext.error("No object file provider found for %s", target.getObjectType());
} else {
ObjectFileProvider objectFileProvider = optionalProvider.get();
Iterator toolChains = CToolChain.findAllCToolChains(target, t -> true, Main.class.getClassLoader()).iterator();
if (! toolChains.hasNext()) {
initialContext.error("No working C compiler found");
} else {
CToolChain toolChain = toolChains.next();
builder.setToolChain(toolChain);
// probe the basic system sizes
CProbe.Builder probeBuilder = CProbe.builder();
probeBuilder.include("");
probeBuilder.include("");
// size and signedness of char
CProbe.Type char_t = CProbe.Type.builder().setName("char").build();
probeBuilder.probeType(char_t);
// int sizes
CProbe.Type int8_t = CProbe.Type.builder().setName("int8_t").build();
probeBuilder.probeType(int8_t);
CProbe.Type int16_t = CProbe.Type.builder().setName("int16_t").build();
probeBuilder.probeType(int16_t);
CProbe.Type int32_t = CProbe.Type.builder().setName("int32_t").build();
probeBuilder.probeType(int32_t);
CProbe.Type int64_t = CProbe.Type.builder().setName("int64_t").build();
probeBuilder.probeType(int64_t);
// float sizes
CProbe.Type float_t = CProbe.Type.builder().setName("float").build();
probeBuilder.probeType(float_t);
CProbe.Type double_t = CProbe.Type.builder().setName("double").build();
probeBuilder.probeType(double_t);
// bool
CProbe.Type _Bool = CProbe.Type.builder().setName("_Bool").build();
probeBuilder.probeType(_Bool);
// pointer
CProbe.Type void_p = CProbe.Type.builder().setName("void *").build();
probeBuilder.probeType(void_p);
// number of bits in char
probeBuilder.probeConstant("CHAR_BIT");
// max alignment
CProbe.Type max_align_t = CProbe.Type.builder().setName("max_align_t").build();
probeBuilder.probeType(max_align_t);
// execute
CProbe probe = probeBuilder.build();
try {
CProbe.Result probeResult = probe.run(toolChain, objectFileProvider, initialContext);
if (probeResult == null) {
initialContext.error("Type system probe compiler execution failed");
} else {
long charSize = probeResult.getTypeInfo(char_t).getSize();
if (charSize != 1) {
initialContext.error("Unexpected size of `char`: %d", Long.valueOf(charSize));
}
TypeSystem.Builder tsBuilder = TypeSystem.builder();
tsBuilder.setBoolSize((int) probeResult.getTypeInfo(_Bool).getSize());
tsBuilder.setBoolAlignment((int) probeResult.getTypeInfo(_Bool).getAlign());
tsBuilder.setByteBits(probeResult.getConstantInfo("CHAR_BIT").getValueAsInt());
tsBuilder.setSignedChar(probeResult.getTypeInfo(char_t).isSigned());
tsBuilder.setInt8Size((int) probeResult.getTypeInfo(int8_t).getSize());
tsBuilder.setInt8Alignment((int) probeResult.getTypeInfo(int8_t).getAlign());
tsBuilder.setInt16Size((int) probeResult.getTypeInfo(int16_t).getSize());
tsBuilder.setInt16Alignment((int) probeResult.getTypeInfo(int16_t).getAlign());
tsBuilder.setInt32Size((int) probeResult.getTypeInfo(int32_t).getSize());
tsBuilder.setInt32Alignment((int) probeResult.getTypeInfo(int32_t).getAlign());
tsBuilder.setInt64Size((int) probeResult.getTypeInfo(int64_t).getSize());
tsBuilder.setInt64Alignment((int) probeResult.getTypeInfo(int64_t).getAlign());
tsBuilder.setFloat32Size((int) probeResult.getTypeInfo(float_t).getSize());
tsBuilder.setFloat32Alignment((int) probeResult.getTypeInfo(float_t).getAlign());
tsBuilder.setFloat64Size((int) probeResult.getTypeInfo(double_t).getSize());
tsBuilder.setFloat64Alignment((int) probeResult.getTypeInfo(double_t).getAlign());
tsBuilder.setPointerSize((int) probeResult.getTypeInfo(void_p).getSize());
tsBuilder.setPointerAlignment((int) probeResult.getTypeInfo(void_p).getAlign());
tsBuilder.setMaxAlignment((int) probeResult.getTypeInfo(max_align_t).getAlign());
// todo: function alignment probe
// for now, references == pointers
tsBuilder.setReferenceSize((int) probeResult.getTypeInfo(void_p).getSize());
tsBuilder.setReferenceAlignment((int) probeResult.getTypeInfo(void_p).getAlign());
CProbe.Type type_id_type = smallTypeIds ? int16_t : int32_t;
tsBuilder.setTypeIdSize((int) probeResult.getTypeInfo(type_id_type).getSize());
tsBuilder.setTypeIdAlignment((int) probeResult.getTypeInfo(type_id_type).getAlign());
tsBuilder.setEndianness(probeResult.getByteOrder());
if (nogc) {
new NoGcTypeSystemConfigurator().accept(tsBuilder);
}
builder.setTypeSystem(tsBuilder.build());
// add additional manual initializers by chaining `.andThen(...)`
builder.setVmFactory(cc -> {
CoreClasses.init(cc);
ThrowExceptionHelper.init(cc);
return VmImpl.create(cc,
new BasicHeaderManualInitializer(cc)
);
});
builder.setObjectFileProvider(objectFileProvider);
ServiceLoader loader = ServiceLoader.load(DriverPlugin.class);
Iterator iterator = loader.iterator();
for (;;) try {
if (! iterator.hasNext()) {
break;
}
DriverPlugin plugin = iterator.next();
plugin.accept(builder);
} catch (ServiceConfigurationError error) {
initialContext.error(error, "Failed to load plugin");
}
errors = initialContext.errors();
if (errors == 0 && llvm) {
Iterator llvmTools = LlvmToolChain.findAllLlvmToolChains(target, t -> true, Main.class.getClassLoader()).iterator();
LlvmToolChain llvmToolChain = null;
while (llvmTools.hasNext()) {
llvmToolChain = llvmTools.next();
if (llvmToolChain.compareVersionTo("12") >= 0) {
break;
}
llvmToolChain = null;
}
if (llvmToolChain == null) {
initialContext.error("No working LLVM toolchain found");
errors = initialContext.errors();
} else {
builder.setLlvmToolChain(llvmToolChain);
}
}
if (errors == 0) {
assert mainClass != null; // else errors would be != 0
// keep it simple to start with
builder.setMainClass(mainClass.replace('.', '/'));
builder.addTypeBuilderFactory(ExternExportTypeBuilder::new);
builder.addTypeBuilderFactory(NativeTypeBuilder::new);
builder.addTypeBuilderFactory(ThreadLocalTypeBuilder::new);
builder.addTypeBuilderFactory(CoreAnnotationTypeBuilder::new);
builder.addTypeBuilderFactory(ReachabilityAnnotationTypeBuilder::new);
builder.addTypeBuilderFactory(Patcher::getTypeBuilder);
builder.addTypeBuilderFactory(AccessorTypeBuilder::new);
builder.setClassContextListener(Patcher::initialize);
builder.addNativeMethodConfiguratorFactory(NativeBindingMethodConfigurator::new);
builder.addResolverFactory(PatcherTypeResolver::create);
builder.addResolverFactory(ConstTypeResolver::new);
builder.addResolverFactory(FunctionTypeResolver::new);
builder.addResolverFactory(PointerTypeResolver::new);
builder.addResolverFactory(InternalNativeTypeResolver::new);
builder.addResolverFactory(NativeTypeResolver::new);
builder.addTaskWrapperFactory(Phase.ADD, next -> (wrapper, ctxt) -> {
Vm vm = ctxt.getVm();
vm.doAttached(vm.newThread(Thread.currentThread().getName(), vm.getMainThreadGroup(), false, Thread.currentThread().getPriority()), () -> wrapper.accept(ctxt));
});
if (llvm) {
builder.addPreHook(Phase.ADD, LLVMIntrinsics::register);
}
builder.addPreHook(Phase.ADD, CoreIntrinsics::register);
builder.addPreHook(Phase.ADD, CoreClasses::get);
builder.addPreHook(Phase.ADD, ReflectionIntrinsics::register);
builder.addPreHook(Phase.ADD, Reflection::get);
builder.addPreHook(Phase.ADD, ThrowExceptionHelper::get);
builder.addPreHook(Phase.ADD, GcCommon::registerIntrinsics);
builder.addPreHook(Phase.ADD, compilationContext -> {
Vm vm = compilationContext.getVm();
VmThread initThread = vm.newThread("initialization", vm.getMainThreadGroup(), false, Thread.currentThread().getPriority());
vm.doAttached(initThread, vm::initialize);
});
builder.addPreHook(Phase.ADD, VIO::get);
builder.addPreHook(Phase.ADD, VFS::initialize);
builder.addPreHook(Phase.ADD, Main::mountInitialFileSystem);
builder.addPreHook(Phase.ADD, new VMHelpersSetupHook());
builder.addPreHook(Phase.ADD, new InitAppClassLoaderHook());
builder.addPreHook(Phase.ADD, compilationContext -> {
Vm vm = compilationContext.getVm();
VmThread initThread = vm.newThread("initialization 2", vm.getMainThreadGroup(), false, Thread.currentThread().getPriority());
vm.doAttached(initThread, vm::initialize2);
});
builder.addPreHook(Phase.ADD, new AddMainClassHook());
if (nogc) {
builder.addPreHook(Phase.ADD, new NoGcSetupHook());
}
builder.addPreHook(Phase.ADD, ReachabilityInfo::forceCoreClassesReachable);
builder.addPreHook(Phase.ADD, compilationContext -> {
FeatureProcessor.processBuildFeature(compilationContext, buildFeatures, hostAppClassLoader);
});
builder.addElementHandler(Phase.ADD, new ElementBodyCreator());
builder.addElementHandler(Phase.ADD, new BuildTimeOnlyElementHandler());
builder.addElementHandler(Phase.ADD, new ElementVisitorAdapter(new DotGenerator(Phase.ADD, graphGenConfig)));
builder.addElementHandler(Phase.ADD, new ElementInitializer());
builder.addElementHandler(Phase.ADD, elem -> ReachabilityInfo.processReachableElement(elem)); // In the add phase, elements may be enqueued via multiple paths. This makes sure Reachability Analysis sees them.
builder.addElementHandler(Phase.ADD, new ReflectiveMethodAccessorGenerator());
builder.addBuilderFactory(Phase.ADD, BuilderStage.TRANSFORM, IntrinsicBasicBlockBuilder::createForAddPhase);
if (nogc) {
builder.addBuilderFactory(Phase.ADD, BuilderStage.TRANSFORM, MultiNewArrayExpansionBasicBlockBuilder::new);
}
builder.addBuilderFactory(Phase.ADD, BuilderStage.TRANSFORM, PatcherResolverBasicBlockBuilder::createIfNeeded);
builder.addBuilderFactory(Phase.ADD, BuilderStage.TRANSFORM, ClassLoadingBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.ADD, BuilderStage.TRANSFORM, NativeBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.ADD, BuilderStage.TRANSFORM, VarHandleResolvingBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.ADD, BuilderStage.TRANSFORM, MemberResolvingBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.ADD, BuilderStage.TRANSFORM, AccessorBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.ADD, BuilderStage.TRANSFORM, StructMemberAccessBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.ADD, BuilderStage.TRANSFORM, PointerBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.ADD, BuilderStage.TRANSFORM, ClassInitializingBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.ADD, BuilderStage.TRANSFORM, ConstantDefiningBasicBlockBuilder::createIfNeeded);
builder.addBuilderFactory(Phase.ADD, BuilderStage.TRANSFORM, ConstantBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.ADD, BuilderStage.TRANSFORM, ArrayLengthBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.ADD, BuilderStage.TRANSFORM, DevirtualizingBasicBlockBuilder::new);
if (optMemoryTracking) {
// TODO: breaks addr_of; should only be done in ANALYZE and then only if addr_of wasn't taken (alias)
// builder.addBuilderFactory(Phase.ADD, BuilderStage.TRANSFORM, LocalMemoryTrackingBasicBlockBuilder::new);
}
builder.addBuilderFactory(Phase.ADD, BuilderStage.CORRECT, RuntimeChecksBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.ADD, BuilderStage.CORRECT, LocalThrowHandlingBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.ADD, BuilderStage.CORRECT, SynchronizedMethodBasicBlockBuilder::createIfNeeded);
builder.addBuilderFactory(Phase.ADD, BuilderStage.OPTIMIZE, SimpleOptBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.ADD, BuilderStage.INTEGRITY, ReachabilityBlockBuilder::new);
builder.addBuilderFactory(Phase.ADD, BuilderStage.INTEGRITY, StaticChecksBasicBlockBuilder::new);
builder.addPostHook(Phase.ADD, ctxt -> {
Vm vm = ctxt.getVm();
vm.doAttached(vm.newThread("FieldAccessor Generation", vm.getMainThreadGroup(), false, Thread.currentThread().getPriority()), () -> {
Reflection.get(ctxt).generateFieldAccessors();
});
});
builder.addPostHook(Phase.ADD, ctxt -> Reflection.get(ctxt).transferToReflectionData());
builder.addPostHook(Phase.ADD, ReachabilityInfo::reportStats);
builder.addPostHook(Phase.ADD, ReachabilityInfo::clear);
builder.addPreHook(Phase.ANALYZE, new VMHelpersSetupHook());
builder.addPreHook(Phase.ANALYZE, ReachabilityInfo::forceCoreClassesReachable);
builder.addPreHook(Phase.ANALYZE, ReachabilityRoots::processReachabilityRoots);
builder.addElementHandler(Phase.ANALYZE, new ElementBodyCopier());
if (optEscapeAnalysis) {
builder.addElementHandler(Phase.ANALYZE, new ElementVisitorAdapter(new EscapeAnalysisIntraMethodAnalysis()));
builder.addElementHandler(Phase.ANALYZE, new ElementVisitorAdapter(
new DotGenerator(Phase.ANALYZE, "analyze-intra", graphGenConfig).addVisitorFactory(EscapeAnalysisDotVisitor::new))
);
} else {
builder.addElementHandler(Phase.ANALYZE, new ElementVisitorAdapter(new DotGenerator(Phase.ANALYZE, graphGenConfig)));
}
if (optGotos) {
builder.addCopyFactory(Phase.ANALYZE, GotoRemovingVisitor::new);
}
if (optPhis) {
builder.addCopyFactory(Phase.ANALYZE, PhiOptimizerVisitor::new);
}
builder.addBuilderFactory(Phase.ANALYZE, BuilderStage.TRANSFORM, IntrinsicBasicBlockBuilder::createForAnalyzePhase);
builder.addBuilderFactory(Phase.ANALYZE, BuilderStage.TRANSFORM, FinalFieldLoadOptimizer::new);
builder.addBuilderFactory(Phase.ANALYZE, BuilderStage.TRANSFORM, ThreadLocalBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.ANALYZE, BuilderStage.TRANSFORM, DevirtualizingBasicBlockBuilder::new);
if (optMemoryTracking) {
builder.addBuilderFactory(Phase.ANALYZE, BuilderStage.TRANSFORM, LocalMemoryTrackingBasicBlockBuilder::new);
}
builder.addBuilderFactory(Phase.ANALYZE, BuilderStage.CORRECT, NumericalConversionBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.ANALYZE, BuilderStage.OPTIMIZE, SimpleOptBasicBlockBuilder::new);
if (optInlining) {
builder.addBuilderFactory(Phase.ANALYZE, BuilderStage.OPTIMIZE, InliningBasicBlockBuilder::new);
}
builder.addBuilderFactory(Phase.ANALYZE, BuilderStage.INTEGRITY, ReachabilityBlockBuilder::new);
builder.addBuilderFactory(Phase.ANALYZE, BuilderStage.INTEGRITY, LocalVariableFindingBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.ANALYZE, BuilderStage.INTEGRITY, StaticChecksBasicBlockBuilder::new);
builder.addPostHook(Phase.ANALYZE, ReachabilityInfo::reportStats);
if (optEscapeAnalysis) {
builder.addPostHook(Phase.ANALYZE, new EscapeAnalysisInterMethodAnalysis());
builder.addPostHook(Phase.ANALYZE, new EscapeAnalysisDotGenerator(graphGenConfig));
}
builder.addPostHook(Phase.ANALYZE, new DispatchTableBuilder());
builder.addPostHook(Phase.ANALYZE, new SupersDisplayBuilder());
builder.addPreHook(Phase.LOWER, ReachabilityRoots::enqueueReachabilityRoots);
builder.addPreHook(Phase.LOWER, new ClassObjectSerializer());
if (optEscapeAnalysis) {
builder.addCopyFactory(Phase.LOWER, EscapeAnalysisOptimizeVisitor::new);
}
builder.addElementHandler(Phase.LOWER, new FunctionLoweringElementHandler());
builder.addElementHandler(Phase.LOWER, new ElementVisitorAdapter(new DotGenerator(Phase.LOWER, graphGenConfig)));
if (optGotos) {
builder.addCopyFactory(Phase.LOWER, GotoRemovingVisitor::new);
}
if (optPhis) {
builder.addCopyFactory(Phase.LOWER, PhiOptimizerVisitor::new);
}
builder.addCopyFactory(Phase.LOWER, BooleanAccessCopier::new);
builder.addCopyFactory(Phase.LOWER, MemberPointerCopier::new);
builder.addCopyFactory(Phase.LOWER, ObjectLiteralSerializingVisitor::new);
if (isWasm) {
builder.addBuilderFactory(Phase.LOWER, BuilderStage.TRANSFORM, AbortingThrowLoweringBasicBlockBuilder::new);
} else {
builder.addBuilderFactory(Phase.LOWER, BuilderStage.TRANSFORM, ThrowLoweringBasicBlockBuilder::new);
}
builder.addBuilderFactory(Phase.LOWER, BuilderStage.TRANSFORM, DevirtualizingBasicBlockBuilder::new);
if (nogc) {
builder.addBuilderFactory(Phase.LOWER, BuilderStage.TRANSFORM, NoGcBasicBlockBuilder::new);
}
builder.addBuilderFactory(Phase.LOWER, BuilderStage.TRANSFORM, IntrinsicBasicBlockBuilder::createForLowerPhase);
builder.addBuilderFactory(Phase.LOWER, BuilderStage.TRANSFORM, InvocationLoweringBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.LOWER, BuilderStage.TRANSFORM, LocalVariableLoweringBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.LOWER, BuilderStage.TRANSFORM, StaticFieldLoweringBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.LOWER, BuilderStage.TRANSFORM, InstanceOfCheckCastBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.LOWER, BuilderStage.TRANSFORM, InitCheckLoweringBasicBlockBuilder::new);
builder.addBuilderFactory(Phase.LOWER, BuilderStage.TRANSFORM, ObjectAccessLoweringBuilder::new);
builder.addBuilderFactory(Phase.LOWER, BuilderStage.TRANSFORM, ObjectMonitorBasicBlockBuilder::new);
if (llvm) {
builder.addBuilderFactory(Phase.LOWER, BuilderStage.TRANSFORM, LLVMCompatibleBasicBlockBuilder::new);
}
builder.addBuilderFactory(Phase.LOWER, BuilderStage.OPTIMIZE, SimpleOptBasicBlockBuilder::new);
if (optMemoryTracking) {
builder.addBuilderFactory(Phase.LOWER, BuilderStage.TRANSFORM, LocalMemoryTrackingBasicBlockBuilder::new);
}
builder.addBuilderFactory(Phase.LOWER, BuilderStage.INTEGRITY, LowerVerificationBasicBlockBuilder::new);
// To avoid serializing Strings we won't need, MethodDataStringsSerializer should be the last "real" BBB
builder.addBuilderFactory(Phase.LOWER, BuilderStage.TRANSFORM, MethodDataStringsSerializer::new);
builder.addBuilderFactory(Phase.LOWER, BuilderStage.INTEGRITY, StaticChecksBasicBlockBuilder::new);
builder.addPostHook(Phase.LOWER, NativeXtorLoweringHook::process);
builder.addPostHook(Phase.LOWER, BuildtimeHeap::reportStats);
LLVMReferencePointerFactory referencePointerFactory =
isWasm ? LLVMReferencePointerFactory.SIMPLE : LLVMReferencePointerFactory.COLLECTED;
LLVMCompiler.Factory llvmCompilerFactory =
isWasm? LLVMEmscriptenCompiler::new : LLVMCompilerImpl::new;
builder.addPreHook(Phase.GENERATE, new StringInternTableEmitter());
builder.addPreHook(Phase.GENERATE, new SupersDisplayEmitter());
builder.addPreHook(Phase.GENERATE, new DispatchTableEmitter());
if (llvm) {
builder.addPreHook(Phase.GENERATE, new LLVMGenerator(isPie ? 2 : 0, isPie ? 2 : 0, referencePointerFactory));
}
builder.addPostHook(Phase.GENERATE, new DotGenerator(Phase.GENERATE, graphGenConfig));
if (compileOutput) {
if (llvm) {
builder.addPostHook(Phase.GENERATE, new LLVMCompileStage(isPie, llvmCompilerFactory, optOptions, llcOptions));
}
}
if (llvm) {
builder.addPostHook(Phase.GENERATE, new MethodDataEmitter());
builder.addPostHook(Phase.GENERATE, new LLVMDefaultModuleCompileStage(isPie, compileOutput, referencePointerFactory, llvmCompilerFactory, optOptions, llcOptions));
if (! isWasm) {
builder.addPostHook(Phase.GENERATE, new LLVMStripStackMapStage());
}
}
if (compileOutput) {
Consumer linkStage =
isWasm ? new EmscriptenLinkStage(outputName, isPie, librarySearchPaths) : new LinkStage(outputName, isPie, librarySearchPaths);
builder.addPostHook(Phase.GENERATE, linkStage);
}
CompilationContext ctxt;
try (Driver driver = builder.build()) {
ctxt = driver.getCompilationContext();
MainMethod.get(ctxt).setMainClass(mainClass);
driver.execute();
}
}
}
} catch (IOException e) {
initialContext.error(e, "Failed to probe system types from tool chain");
}
}
}
}
return;
}
private ClassPathEntry getCoreComponent(final String artifactId) {
return ClassPathEntry.of(new DefaultArtifact("org.qbicc", artifactId, "jar", MainProperties.QBICC_VERSION));
}
private void resolveClassPath(DiagnosticContext ctxt, Consumer classPathItemConsumer, final List paths) throws IOException {
QbiccMavenResolver resolver = new QbiccMavenResolver(new QbiccServiceLocator());
File globalSettings = resolver.getGlobalSettings();
File userSettings = resolver.getUserSettings();
Settings settings;
try {
settings = resolver.createSettings(ctxt, globalSettings, userSettings);
} catch (SettingsBuildingException e) {
throw new IOException(e);
}
RepositorySystemSession session = resolver.createSession(settings);
List result = resolver.requestArtifacts(session, settings, paths, ctxt);
result.forEach(classPathItemConsumer);
}
static List splitPathString(String str) {
if (str == null || str.isEmpty()) {
return List.of();
}
char psc = File.pathSeparatorChar;
int start = 0;
int idx = str.indexOf(psc);
ArrayList list = new ArrayList<>();
for (;;) {
String subStr;
if (idx == -1) {
subStr = str.substring(start);
} else {
subStr = str.substring(start, idx);
}
if (! subStr.isEmpty()) {
list.add(Path.of(subStr));
}
if (idx == -1) {
return list;
} else {
start = idx + 1;
}
idx = str.indexOf(psc, start);
}
}
private static void mountInitialFileSystem(CompilationContext ctxt) {
// install all boot classpath items into the VFS
VFS vfs = VFS.get(ctxt);
VirtualFileSystem fileSystem = vfs.getFileSystem();
Driver driver = Driver.get(ctxt);
AbsoluteVirtualPath javaHome = vfs.getQbiccPath().resolve("java.home");
try {
//noinspection OctalInteger
fileSystem.mkdirs(javaHome, 0755);
} catch (IOException e) {
ctxt.error(e, "Failed to create %s", javaHome);
}
AbsoluteVirtualPath modulesPath = javaHome.resolve("modules");
Collection bootModuleNames = driver.getBootModuleNames();
for (String bootModuleName : bootModuleNames) {
ClassPathItem bootItem = driver.getBootModuleClassPathItem(bootModuleName);
try {
bootItem.mount(fileSystem, modulesPath.resolve(bootModuleName));
} catch (IOException e) {
ctxt.error(e, "Failed to mount %s", bootItem);
}
}
// now look for all META-INF/java.home files and link them into the main system
for (String bootModuleName : bootModuleNames) {
AbsoluteVirtualPath moduleJavaHome = modulesPath.resolve(bootModuleName).resolve("META-INF").resolve("java.home");
try {
int attrs = fileSystem.getBooleanAttributes(moduleJavaHome, false);
if ((attrs & VFSUtils.BA_EXISTS) != 0) {
// do it
linkIn(fileSystem, javaHome, moduleJavaHome);
}
} catch (IOException e) {
ctxt.error(e, "Failed to remount %s", moduleJavaHome);
}
}
}
private static void linkIn(final VirtualFileSystem fileSystem, final AbsoluteVirtualPath toDir, final AbsoluteVirtualPath fromDir) throws IOException {
Collection names = fileSystem.getDirectoryEntries(fromDir, false);
for (String name : names) {
AbsoluteVirtualPath fromPath = fromDir.resolve(name);
AbsoluteVirtualPath toPath = toDir.resolve(name);
//noinspection OctalInteger
fileSystem.mkdirs(toPath.getParent(), 0755);
if ((fileSystem.getBooleanAttributes(fromPath, false) & VFSUtils.BA_DIRECTORY) != 0) {
linkIn(fileSystem, toPath, fromPath);
} else {
fileSystem.link(toPath, fromPath, false);
}
}
}
public static void main(String[] args) {
System.setProperty("java.util.logging.manager", LogManager.class.getName());
CommandLineProcessor optionsProcessor = new CommandLineProcessor();
CmdResult result = optionsProcessor.process(args);
if (result != CmdResult.CMD_RESULT_OK) {
return;
}
if (optionsProcessor.mainClass.isEmpty() && optionsProcessor.inputJar == null) {
System.err.println("Must either provide a or use --jar ");
return;
}
Builder mainBuilder = builder();
mainBuilder.setClassLibVersion(optionsProcessor.rtVersion)
.appendBootPaths(optionsProcessor.appendedBootPathEntries)
.prependBootPaths(optionsProcessor.prependedBootPathEntries)
.addAppPaths(optionsProcessor.appPathEntries)
.processJarArgument(optionsProcessor.inputJar)
.addBuildFeatures(optionsProcessor.buildFeatures)
.setOutputPath(optionsProcessor.outputPath)
.setOutputName(optionsProcessor.outputName)
.setMainClass(optionsProcessor.mainClass)
.setDiagnosticsHandler(diagnostics -> {
for (Diagnostic diagnostic : diagnostics) {
try {
diagnostic.appendTo(System.err);
} catch (IOException e) {
// just give up
break;
}
}
})
.setGc(optionsProcessor.gc.toString())
.setIsPie(optionsProcessor.isPie)
.setCompileOutput(optionsProcessor.compileOutput)
.setOptMemoryTracking(optionsProcessor.optArgs.optMemoryTracking)
.setOptInlining(optionsProcessor.optArgs.optInlining)
.setOptGotos(optionsProcessor.optArgs.optGotos)
.setOptPhis(optionsProcessor.optArgs.optPhis)
.setOptEscapeAnalysis(optionsProcessor.optArgs.optEscapeAnalysis)
.setSmallTypeIds(optionsProcessor.smallTypeIds)
.setBackend(optionsProcessor.backend)
.setGraphGenConfig(optionsProcessor.graphGenConfig)
.setOptOptions(optionsProcessor.optOptions)
.setLlcOptions(optionsProcessor.llcOptions)
.addLibrarySearchPaths(splitPathString(System.getenv("LIBRARY_PATH")))
.addLibrarySearchPaths(optionsProcessor.libSearchPaths);
Platform platform = optionsProcessor.platform;
if (platform != null) {
mainBuilder.setPlatform(platform);
}
Main main = mainBuilder.build();
DiagnosticContext context = main.call();
int errors = context.errors();
int warnings = context.warnings();
if (errors > 0) {
if (warnings > 0) {
System.err.printf("Compilation failed with %d error(s) and %d warning(s)%n", Integer.valueOf(errors), Integer.valueOf(warnings));
} else {
System.err.printf("Compilation failed with %d error(s)%n", Integer.valueOf(errors));
}
} else if (warnings > 0) {
System.err.printf("Compilation completed with %d warning(s)%n", Integer.valueOf(warnings));
}
System.exit(errors == 0 ? 0 : 1);
}
private enum CmdResult {
CMD_RESULT_HELP,
CMD_RESULT_OK,
CMD_RESULT_ERROR,
;
}
@CommandLine.Command(versionProvider = VersionProvider.class, mixinStandardHelpOptions = true)
private static final class CommandLineProcessor {
private enum GCType {
NONE("none"),
;
private final String gcType;
GCType(String type) {
this.gcType = type;
}
public String toString() {
return gcType;
}
}
@CommandLine.Option(names = "--boot-path-prepend-artifact", converter = ClassPathEntry.MavenArtifact.Converter.class)
void prependBootPathArtifact(List artifact) {
prependedBootPathEntries.addAll(artifact);
}
@CommandLine.Option(names = "--boot-path-prepend-file", converter = ClassPathEntry.FilePath.Converter.class)
void prependBootPathFile(List filePath) {
prependedBootPathEntries.addAll(filePath);
}
private final List prependedBootPathEntries = new ArrayList<>();
@CommandLine.Option(names = "--boot-path-append-artifact", converter = ClassPathEntry.MavenArtifact.Converter.class)
void appendBootPathArtifact(List artifact) {
appendedBootPathEntries.addAll(artifact);
}
@CommandLine.Option(names = "--boot-path-append-file", converter = ClassPathEntry.FilePath.Converter.class)
void appendBootPathFile(List filePath) {
appendedBootPathEntries.addAll(filePath);
}
private final List appendedBootPathEntries = new ArrayList<>();
@CommandLine.Option(names = "--rt-version")
private String rtVersion = MainProperties.CLASSLIB_DEFAULT_VERSION;
@CommandLine.Option(names = "--app-path-artifact", converter = ClassPathEntry.MavenArtifact.Converter.class)
void addAppPathArtifact(List artifact) {
appPathEntries.addAll(artifact);
}
@CommandLine.Option(names = "--app-path-file", converter = ClassPathEntry.FilePath.Converter.class)
void addAppPathFile(List filePath) {
appPathEntries.addAll(filePath);
}
private final List appPathEntries = new ArrayList<>();
@CommandLine.Option(names ="--build-feature", description = "GraalVM native-image Feature")
void addBuildFeature(List features) { buildFeatures.addAll(features); }
private final List buildFeatures = new ArrayList<>();
@CommandLine.Option(names = "--jar", description = "Compile an executable jar")
private Path inputJar;
@CommandLine.Option(names = "--output-path", description = "Specify directory where build files are placed")
private Path outputPath;
@CommandLine.Option(names = { "--output-name", "-o" }, defaultValue = "a.out", description = "Specify the name of the output executable file or library")
private String outputName;
@CommandLine.Option(names = "--no-compile-output", negatable = true, defaultValue = "true", description = "Enable/disable llvm compilation of output files")
boolean compileOutput;
@CommandLine.Option(names = "--debug")
private boolean debug;
@CommandLine.Option(names = "--debug-vtables")
private boolean debugVTables;
@CommandLine.Option(names = "--dispatch-stats")
private boolean dispatchStats;
@CommandLine.Option(names = "--debug-reachability")
private boolean debugReachability;
@CommandLine.Option(names = "--debug-supers")
private boolean debugSupers;
@CommandLine.Option(names = "--debug-devirt")
private boolean debugDevirt;
@CommandLine.Option(names = "--debug-interpreter")
private boolean debugInterpreter;
@CommandLine.Option(names = "--gc", defaultValue = "none", description = "Type of GC to use. Valid values: ${COMPLETION-CANDIDATES}")
private GCType gc;
@CommandLine.Option(names = "--heap-stats")
private boolean heapStats;
@CommandLine.Option(names = "--method-data-stats")
private boolean methodDataStats;
@CommandLine.Option(names = "--pie", negatable = true, defaultValue = "false", description = "[Disable|Enable] generation of position independent executable")
private boolean isPie;
@CommandLine.Option(names = "--platform", converter = PlatformConverter.class)
private Platform platform;
@CommandLine.Option(names = "--string-pool-stats")
private boolean stringPoolStats;
@CommandLine.Option(names = { "--library-search-path", "-L" }, description = "Additional library search paths")
private List libSearchPaths;
@CommandLine.Option(names = "--small-type-ids", negatable = true, defaultValue = "false", description = "Use narrow (16-bit) type ID values if true, wide (32-bit) type ID values if false")
private boolean smallTypeIds;
@CommandLine.Option(names = "--backend", defaultValue = "llvm", description = "The backend type to use. Valid values: ${COMPLETION-CANDIDATES}")
private Backend backend;
@CommandLine.Parameters(index="0", arity="1", defaultValue = "", description = "Application main class")
private String mainClass;
@CommandLine.ArgGroup(exclusive = false, multiplicity = "0..1", heading = "Options for controlling generation of graphs for methods%n")
private GraphGenArgs graphGenArgs;
@CommandLine.ArgGroup(exclusive = false, heading = "Options for controlling optimizations%n")
private OptArgs optArgs = new OptArgs();
private GraphGenConfig graphGenConfig = new GraphGenConfig();
private static class GraphGenArgs {
@CommandLine.Option(names = { "-g", "--gen-graph"}, required = true, description = "Enable generation of graphs")
boolean genGraph;
@CommandLine.ArgGroup(exclusive=false, multiplicity = "0..*")
List methodsAndPhases;
}
private static class GraphGenMethodsPhases {
@CommandLine.Option(names = { "-m", "--methods"}, required = false, split = ",", defaultValue = GraphGenConfig.ALL_METHODS,
description = "List of methods separated by comma. Default: ${DEFAULT-VALUE}")
List methods;
@CommandLine.Option(names = { "-p", "--phases" }, required = false, split = ",", defaultValue = GraphGenConfig.ALL_PHASES,
description = "List of phases separated by comma. Default: ${DEFAULT-VALUE}")
List phases;
}
@CommandLine.Option(names = "--llvm-opt-option", split = ",", description = "Pass options to the LLVM opt command")
private List optOptions = new ArrayList();
@CommandLine.Option(names = "--llvm-llc-option", split = ",", description = "Pass options to the LLVM llc command")
private List llcOptions = new ArrayList();
static class OptArgs {
@CommandLine.Option(names = "--opt-memory-tracking", negatable = true, defaultValue = "false", description = "Enable/disable redundant store/load tracking and elimination")
boolean optMemoryTracking;
@CommandLine.Option(names = "--opt-inlining", negatable = true, defaultValue = "false", description = "Enable/disable inliner")
boolean optInlining;
@CommandLine.Option(names = "--no-opt-phis", negatable = true, defaultValue = "true", description = "Enable/disable `phi` elimination")
boolean optPhis;
@CommandLine.Option(names = "--no-opt-gotos", negatable = true, defaultValue = "true", description = "Enable/disable `goto` elimination")
boolean optGotos;
@CommandLine.Option(names = "--escape-analysis", negatable = true, defaultValue = "false", description = "Enable/disable escape analysis")
boolean optEscapeAnalysis;
}
public CmdResult process(String[] args) {
try {
CommandLine commandLine = new CommandLine(this);
ParseResult parseResult = commandLine.parseArgs(args);
if (CommandLine.printHelpIfRequested(parseResult)) {
return CmdResult.CMD_RESULT_HELP;
}
} catch (ParameterException ex) { // command line arguments could not be parsed
System.err.println(ex.getMessage());
ex.getCommandLine().usage(System.err);
return CmdResult.CMD_RESULT_ERROR;
}
if (debug) {
Logger.getLogger("").setLevel(Level.DEBUG);
}
if (debugVTables) {
Logger.getLogger("org.qbicc.plugin.dispatch.tables").setLevel(Level.DEBUG);
}
if (dispatchStats) {
Logger.getLogger("org.qbicc.plugin.dispatch.stats").setLevel(Level.DEBUG);
}
if (debugReachability) {
Logger.getLogger("org.qbicc.plugin.reachability").setLevel(Level.DEBUG);
}
if (debugSupers) {
Logger.getLogger("org.qbicc.plugin.instanceofcheckcast.supers").setLevel(Level.DEBUG);
}
if (debugDevirt) {
Logger.getLogger("org.qbicc.plugin.dispatch.devirt").setLevel(Level.DEBUG);
}
if (debugInterpreter) {
Logger.getLogger("org.qbicc.interpreter").setLevel(Level.DEBUG);
}
if (heapStats) {
Logger.getLogger("org.qbicc.plugin.serialization.stats").setLevel(Level.DEBUG);
}
if (methodDataStats) {
Logger.getLogger("org.qbicc.plugin.methodinfo.stats").setLevel(Level.DEBUG);
}
if (stringPoolStats) {
Logger.getLogger("org.qbicc.plugin.stringpool.stats").setLevel(Level.DEBUG);
}
if (outputPath == null) {
outputPath = Path.of(System.getProperty("java.io.tmpdir"), "qbicc-output-" + Integer.toHexString(ThreadLocalRandom.current().nextInt()));
}
if (graphGenArgs != null && graphGenArgs.genGraph) {
graphGenConfig.setEnabled(true);
if (graphGenArgs.methodsAndPhases == null) {
graphGenConfig.addMethodAndPhase(GraphGenConfig.ALL_METHODS, GraphGenConfig.ALL_PHASES);
} else {
for (GraphGenMethodsPhases option : graphGenArgs.methodsAndPhases) {
for (String method : option.methods) {
for (String phase : option.phases) {
graphGenConfig.addMethodAndPhase(method, phase);
}
}
}
}
}
return CmdResult.CMD_RESULT_OK;
}
}
public static Builder builder() {
return new Builder();
}
public static final class Builder {
private final List bootPathsPrepend = new ArrayList<>();
private final List bootPathsAppend = new ArrayList<>();
private final List appPaths = new ArrayList<>();
private String classLibVersion = MainProperties.CLASSLIB_DEFAULT_VERSION;
private Path outputPath;
private String outputName = "a.out";
private Consumer> diagnosticsHandler = diagnostics -> {};
private Platform platform = Platform.HOST_PLATFORM;
private String mainClass;
private String gc = "none";
// TODO Detect whether the system uses PIEs by default and match that if possible
private boolean isPie = false;
private boolean compileOutput = true;
private boolean optMemoryTracking = false;
private boolean optInlining = false;
private boolean optPhis = true;
private boolean optGotos = true;
private boolean optEscapeAnalysis = false;
private GraphGenConfig graphGenConfig;
private boolean smallTypeIds = false;
private Backend backend = Backend.llvm;
private List librarySearchPaths = List.of();
private List buildFeatures = new ArrayList<>();
private ClassPathResolver classPathResolver;
private List optOptions = new ArrayList<>();
private List llcOptions = new ArrayList<>();
Builder() {}
public Builder appendBootPath(ClassPathEntry entry) {
Assert.checkNotNullParam("entry", entry);
bootPathsAppend.add(entry);
return this;
}
public Builder appendBootPaths(List entry) {
Assert.checkNotNullParam("entry", entry);
bootPathsAppend.addAll(entry);
return this;
}
public Builder prependBootPath(ClassPathEntry entry) {
Assert.checkNotNullParam("entry", entry);
bootPathsPrepend.add(entry);
return this;
}
public Builder prependBootPaths(List entry) {
Assert.checkNotNullParam("entry", entry);
bootPathsPrepend.addAll(entry);
return this;
}
public Builder setClassLibVersion(String classLibVersion) {
Assert.checkNotNullParam("classLibVersion", classLibVersion);
this.classLibVersion = classLibVersion;
return this;
}
public Builder addAppPath(ClassPathEntry entry) {
Assert.checkNotNullParam("entry", entry);
appPaths.add(entry);
return this;
}
public Builder addAppPaths(List entry) {
Assert.checkNotNullParam("entry", entry);
appPaths.addAll(entry);
return this;
}
public Builder addBuildFeatures(List features) {
buildFeatures.addAll(features);
return this;
}
public Builder setOutputPath(Path path) {
Assert.checkNotNullParam("path", path);
this.outputPath = path;
return this;
}
public Builder setOutputName(String outputName) {
Assert.checkNotNullParam("outputName", outputName);
this.outputName = outputName;
return this;
}
public Builder setPlatform(Platform platform) {
Assert.checkNotNullParam("platform", platform);
this.platform = platform;
return this;
}
public Builder setDiagnosticsHandler(Consumer> handler) {
Assert.checkNotNullParam("handler", handler);
diagnosticsHandler = handler;
return this;
}
public Builder processJarArgument(Path inputJar) {
if (inputJar == null) {
return this;
}
try {
JarInputStream jarStream = new JarInputStream(new FileInputStream(inputJar.toAbsolutePath().toString()));
appPaths.add(ClassPathEntry.of(inputJar));
mainClass = jarStream.getManifest().getMainAttributes().getValue("Main-Class");
String classPath = jarStream.getManifest().getMainAttributes().getValue("Class-Path");
if (classPath != null && !classPath.equals("")) {
Path parentDir = inputJar.toAbsolutePath().getParent();
for (String e : classPath.split(" ")) {
if (!e.isEmpty()) {
ClassPathEntry cpe = ClassPathEntry.of(parentDir.resolve(e));
appPaths.add(cpe);
}
}
}
} catch (IOException e) {
System.err.println("Error processing argument \"-jar "+inputJar+"\": "+e.getMessage());
}
return this;
}
public Builder setMainClass(String mainClass) {
if (!mainClass.equals("")) {
this.mainClass = mainClass;
}
return this;
}
public Builder setGc(String gc) {
this.gc = Assert.checkNotNullParam("gc", gc);
return this;
}
public Builder setIsPie(boolean isPie) {
this.isPie = isPie;
return this;
}
public Builder setGraphGenConfig(GraphGenConfig graphGenConfig) {
Assert.checkNotNullParam("graphGenConfig", graphGenConfig);
this.graphGenConfig = graphGenConfig;
return this;
}
public Builder setCompileOutput(boolean compileOutput) {
this.compileOutput = compileOutput;
return this;
}
public Builder setOptMemoryTracking(boolean optMemoryTracking) {
this.optMemoryTracking = optMemoryTracking;
return this;
}
public Builder setOptInlining(boolean optInlining) {
this.optInlining = optInlining;
return this;
}
public Builder setOptPhis(boolean optPhis) {
this.optPhis = optPhis;
return this;
}
public Builder setOptGotos(boolean optGotos) {
this.optGotos = optGotos;
return this;
}
public Builder setOptEscapeAnalysis(boolean optEscapeAnalysis) {
this.optEscapeAnalysis = optEscapeAnalysis;
return this;
}
public Builder setBackend(Backend backend) {
this.backend = Assert.checkNotNullParam("backend", backend);
return this;
}
public Builder setSmallTypeIds(boolean smallTypeIds) {
this.smallTypeIds = smallTypeIds;
return this;
}
public Builder addLibrarySearchPaths(List librarySearchPaths) {
if (librarySearchPaths != null && !librarySearchPaths.isEmpty()) {
if (this.librarySearchPaths.isEmpty()) {
this.librarySearchPaths = List.copyOf(librarySearchPaths);
} else {
Path[] p1 = this.librarySearchPaths.toArray(Path[]::new);
Path[] p2 = librarySearchPaths.toArray(Path[]::new);
Path[] finalPaths = Arrays.copyOf(p1, p1.length + p2.length);
System.arraycopy(p2, 0, finalPaths, p1.length, p2.length);
this.librarySearchPaths = List.of(finalPaths);
}
}
return this;
}
public Builder setClassPathResolver(ClassPathResolver classPathResolver) {
this.classPathResolver = classPathResolver;
return this;
}
public Builder setOptOptions(List cmd) {
optOptions.addAll(cmd);
return this;
}
public Builder setLlcOptions(List cmd) {
llcOptions.addAll(cmd);
return this;
}
public Main build() {
return new Main(this);
}
}
static class MainProperties {
static final String CLASSLIB_DEFAULT_VERSION;
static final String QBICC_VERSION;
static {
Properties properties = new Properties();
InputStream inputStream = MainProperties.class.getClassLoader().getResourceAsStream("main.properties");
if (inputStream == null) {
throw new Error("Missing main.properties");
} else try (inputStream) {
try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
try (BufferedReader br = new BufferedReader(reader)) {
properties.load(br);
}
}
} catch (IOException e) {
throw new IOError(e);
}
CLASSLIB_DEFAULT_VERSION = properties.getProperty("classlib.default-version");
QBICC_VERSION = properties.getProperty("qbicc.version");
}
}
static class VersionProvider implements CommandLine.IVersionProvider {
@Override
public String[] getVersion() {
return new String[] { "Qbicc version " + MainProperties.QBICC_VERSION };
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy