diff options
| author | Michael Williamson <mike@zwobble.org> | 2026-04-06 14:06:15 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2026-04-06 14:06:15 +0100 |
| commit | d11f5583d0f3b941dfe7b27e9e80d051a0a7231e (patch) | |
| tree | f0d652daea9e5e96992105c3665c47f9c3806b7e | |
| parent | 0b8637f445a6e3483dabe091742012f528457fe9 (diff) | |
Add full backend support for cranelift
5 files changed, 204 insertions, 10 deletions
diff --git a/patio-to-cranelift/src/codegen/mod.rs b/patio-to-cranelift/src/codegen/mod.rs index 85fbae6c..7fad42f5 100644 --- a/patio-to-cranelift/src/codegen/mod.rs +++ b/patio-to-cranelift/src/codegen/mod.rs @@ -32,6 +32,7 @@ mod values; pub(crate) fn compile_compilation_unit( compilation_unit: PatioCompilationUnit, + main: Option<&str>, source_path: &Path, target_triple: target_lexicon::Triple, ) -> Result<Vec<u8>, CompileError> { @@ -45,6 +46,33 @@ pub(crate) fn compile_compilation_unit( define_compilation_unit_element(&element, &mut context); } + if let Some(main) = main { + let mut main_signature = new_cranelift_signature(); + main_signature + .returns + .push(cranelift_codegen::ir::AbiParam::new( + cranelift_codegen::ir::types::I32, + )); + + context.declare_function("main", cranelift_module::Linkage::Export, &main_signature); + context.enter_function("main", main_signature, |function_context| { + let func_ref = function_context.func_id(main); + function_context.function_builder.ins().call(func_ref, &[]); + // TODO: we ignore the return value and just return 0, which is fine + // in practice since we use exit() for non-zero exit codes. However, + // we should clarify this behaviour in shid-to-patio, and other + // patio backends. + let exit_code = function_context + .function_builder + .ins() + .iconst(cranelift_codegen::ir::types::I32, 0); + function_context + .function_builder + .ins() + .return_(&[exit_code]); + }); + } + let object = context.into_object(); object.emit().map_err(CompileError::ObjectEmitFailed) } diff --git a/patio-to-cranelift/src/lib.rs b/patio-to-cranelift/src/lib.rs index 22afabba..80d22840 100644 --- a/patio-to-cranelift/src/lib.rs +++ b/patio-to-cranelift/src/lib.rs @@ -14,7 +14,11 @@ mod codegen; mod errors; mod patio; -pub fn compile(source_path: &Path, target_str: &str) -> Result<(), CompileError> { +pub fn compile( + source_path: &Path, + target_str: &str, + main: Option<&str>, +) -> Result<(), CompileError> { let source_bytes = std::fs::read(source_path).map_err(CompileError::ReadSourceBytes)?; let compilation_unit: PatioCompilationUnit = @@ -26,7 +30,7 @@ pub fn compile(source_path: &Path, target_str: &str) -> Result<(), CompileError> target_lexicon::Triple::from_str(target_str).map_err(CompileError::ParseTargetTriple)?; let object_bytes = - codegen::compile_compilation_unit(compilation_unit, source_path, target_triple)?; + codegen::compile_compilation_unit(compilation_unit, main, source_path, target_triple)?; let destination_path = source_path.with_extension("o"); std::fs::write(destination_path, object_bytes).map_err(CompileError::WriteObjectFile)?; diff --git a/patio-to-cranelift/src/main.rs b/patio-to-cranelift/src/main.rs index 77409d1f..e000dbd2 100644 --- a/patio-to-cranelift/src/main.rs +++ b/patio-to-cranelift/src/main.rs @@ -11,12 +11,18 @@ struct Args { /// The LLVM triple to target. #[arg(long)] target: String, + + /// The name of the main function. If set, this will add a C-style `main` + /// function that calls the given main function, and converts the return + /// value into an int32. + #[arg(long)] + main: Option<String>, } fn main() { let args = Args::parse(); for source_path in args.source_paths { - patio_to_cranelift::compile(&source_path, &args.target).unwrap(); + patio_to_cranelift::compile(&source_path, &args.target, args.main.as_deref()).unwrap(); } } diff --git a/src/main/java/org/shedlang/compiler/backends/cranelift/CraneliftBackend.java b/src/main/java/org/shedlang/compiler/backends/cranelift/CraneliftBackend.java index 7a16cc44..584fd7d0 100644 --- a/src/main/java/org/shedlang/compiler/backends/cranelift/CraneliftBackend.java +++ b/src/main/java/org/shedlang/compiler/backends/cranelift/CraneliftBackend.java @@ -1,23 +1,35 @@ package org.shedlang.compiler.backends.cranelift; import com.google.flatbuffers.FlatBufferBuilder; +import org.pcollections.PVector; +import org.shedlang.compiler.backends.Backend; +import org.shedlang.compiler.backends.PatioBackends; import org.shedlang.compiler.backends.llvm.LlvmBackend; import org.shedlang.compiler.backends.llvm.LlvmTarget; import org.shedlang.compiler.backends.paths.PartialTargetPath; +import org.shedlang.compiler.backends.paths.TargetDir; +import org.shedlang.compiler.config.ProfileConfig; +import org.shedlang.compiler.config.ProjectConfig; import org.shedlang.compiler.filesystem.ShedCompilerFiles; import org.shedlang.compiler.patio.PatioCompilationUnit; import org.shedlang.compiler.patio.serialization.PatioToFlatbuffers; import org.shedlang.compiler.runtime.ShedRuntime; +import org.shedlang.compiler.shid.ShidNamespace; +import org.shedlang.compiler.typechecker.TypeInfos; +import org.shedlang.compiler.types.NamespaceName; +import org.shedlang.compiler.util.P; import org.shedlang.compiler.util.processes.ProcessError; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; import static org.shedlang.compiler.util.processes.ProcessRunner.runInheritIo; -public class CraneliftBackend { +public class CraneliftBackend implements Backend { public static final CraneliftBackend AMD64 = forTarget(LlvmTarget.AMD64); public static CraneliftBackend forTarget(LlvmTarget target) { @@ -34,20 +46,47 @@ public class CraneliftBackend { PatioCompilationUnit patio, PartialTargetPath partialTargetPath ) throws IOException, InterruptedException { + return compileCompilationUnit(patio, Optional.empty(), partialTargetPath); + } + + private Path compileMain( + PatioCompilationUnit patio, + String mainFunctionName, + PartialTargetPath partialTargetPath + ) throws IOException, InterruptedException { + return compileCompilationUnit(patio, Optional.of(mainFunctionName), partialTargetPath); + } + + private Path compileCompilationUnit( + PatioCompilationUnit patio, + Optional<String> mainFunctionName, + PartialTargetPath partialTargetPath + ) throws IOException, InterruptedException { var fbsBuilder = new FlatBufferBuilder(); var compilationUnitOffset = PatioToFlatbuffers.serializeCompilationUnit(fbsBuilder, patio); fbsBuilder.finish(compilationUnitOffset); // TODO: avoid the copy var patioPath = partialTargetPath.withExtension(".patio"); + Files.createDirectories(patioPath.getParent()); Files.write(patioPath, fbsBuilder.sizedByteArray()); - runInheritIo(List.of( - ShedCompilerFiles.getRoot().resolve("patio-to-cranelift/target/debug/patio-to-cranelift").toString(), - patioPath.toString(), - "--target", - this.target.targetTriple() - )); + var patioToCraneliftArgs = new ArrayList<String>(); + patioToCraneliftArgs.add( + ShedCompilerFiles.getRoot() + .resolve("patio-to-cranelift/target/debug/patio-to-cranelift") + .toString() + ); + patioToCraneliftArgs.add(patioPath.toString()); + patioToCraneliftArgs.add("--target"); + patioToCraneliftArgs.add(this.target.targetTriple()); + + if (mainFunctionName.isPresent()) { + patioToCraneliftArgs.add("--main"); + patioToCraneliftArgs.add(mainFunctionName.get()); + } + + runInheritIo(patioToCraneliftArgs); var objectFilePath = partialTargetPath.withExtension(".o"); return objectFilePath; @@ -61,4 +100,87 @@ public class CraneliftBackend { // TODO: extract linking if it's not specific to LLVM. LlvmBackend.forTarget(this.target).link(inputs, outputPath, shedRuntime); } + + @Override + public String targetName() { + return "cranelift-" + target.targetName(); + } + + @Override + public void compile( + ProjectConfig config, + ProfileConfig profileConfig, + List<ShidNamespace> namespaces, + TypeInfos typeInfos, + TargetDir targetDir + ) throws IOException, InterruptedException { + PatioBackends.compile( + config, + profileConfig, + namespaces, + typeInfos, + new PatioToBinary( + targetDir, + P.vector() + ) + ); + } + + private class PatioToBinary implements PatioBackends.PatioToTarget { + private final TargetDir targetDir; + private final PVector<Path> objectFilePaths; + + public PatioToBinary( + TargetDir targetDir, + PVector<Path> objectFilePaths + ) { + this.targetDir = targetDir; + this.objectFilePaths = objectFilePaths; + } + + @Override + public PatioBackends.PatioToTarget namespace( + NamespaceName namespaceName, + PatioCompilationUnit patioCompilationUnit + ) throws IOException, InterruptedException { + var objectPath = compileCompilationUnit( + patioCompilationUnit, + this.targetDir.namespace(namespaceName) + ); + + return new PatioToBinary( + this.targetDir, + this.objectFilePaths.plus(objectPath) + ); + } + + @Override + public void bin( + String binName, + PatioCompilationUnit patioCompilationUnit, + String mainFunctionName, + ProfileConfig profileConfig + ) throws IOException, InterruptedException { + var linkInputs = new ArrayList<>(objectFilePaths); + + var mainObjectPath = compileMain( + patioCompilationUnit, + mainFunctionName, + this.targetDir.bin(binName) + ); + linkInputs.add(mainObjectPath); + + var shedRuntime = ShedRuntime.runtime( + target.targetName(), + profileConfig.gc(), + targetDir + ); + + link( + linkInputs, + targetDir.topLevel(binName), + shedRuntime + ); + } + } } diff --git a/src/test/java/org/shedlang/compiler/backends/cranelift/CraneliftAmd64ExampleTests.java b/src/test/java/org/shedlang/compiler/backends/cranelift/CraneliftAmd64ExampleTests.java new file mode 100644 index 00000000..bc78c435 --- /dev/null +++ b/src/test/java/org/shedlang/compiler/backends/cranelift/CraneliftAmd64ExampleTests.java @@ -0,0 +1,34 @@ +package org.shedlang.compiler.backends.cranelift; + +import org.junit.jupiter.api.Disabled; +import org.shedlang.compiler.backends.Backend; +import org.shedlang.compiler.backends.ExampleTests; + +import java.util.List; +import java.util.Set; + +@Disabled +public class CraneliftAmd64ExampleTests extends ExampleTests { + @Override + protected Backend backend() { + return CraneliftBackend.AMD64; + } + + @Override + protected List<String> binaryRunner() { + return List.of(); + } + + @Override + protected String executableExtension() { + return ""; + } + + private static final Set<String> DISABLED = Set.of( + ); + + @Override + protected boolean isDisabled(String testName) { + return DISABLED.contains(testName); + } +} |
