From 1efa3d6f8bad059953cb6a8726bf74d2ee7fa3a0 Mon Sep 17 00:00:00 2001 From: Sam Sartor Date: Fri, 6 Jan 2023 13:53:48 -0700 Subject: [PATCH] Add `clif-util compile` option to output object file (#5493) * add clif-util compile option to output object file * switch from a box to a borrow * update objectmodule tests to use borrowed isa * put targetisa into an arc --- cranelift/codegen/src/isa/aarch64/mod.rs | 2 +- cranelift/codegen/src/isa/mod.rs | 18 +++- cranelift/codegen/src/isa/riscv64/mod.rs | 2 +- cranelift/codegen/src/isa/s390x/mod.rs | 2 +- cranelift/codegen/src/isa/x64/mod.rs | 6 +- cranelift/filetests/src/function_runner.rs | 4 +- cranelift/filetests/src/test_run.rs | 4 +- cranelift/fuzzgen/src/lib.rs | 4 +- cranelift/jit/src/backend.rs | 8 +- cranelift/object/src/backend.rs | 8 +- cranelift/reader/src/isaspec.rs | 4 +- cranelift/reader/src/lib.rs | 4 +- cranelift/src/compile.rs | 100 +++++++++++++++------ cranelift/src/run.rs | 4 +- crates/cranelift/src/compiler.rs | 6 +- 15 files changed, 114 insertions(+), 62 deletions(-) diff --git a/cranelift/codegen/src/isa/aarch64/mod.rs b/cranelift/codegen/src/isa/aarch64/mod.rs index a8a94c23bb..b68fda1661 100644 --- a/cranelift/codegen/src/isa/aarch64/mod.rs +++ b/cranelift/codegen/src/isa/aarch64/mod.rs @@ -216,7 +216,7 @@ pub fn isa_builder(triple: Triple) -> IsaBuilder { constructor: |triple, shared_flags, builder| { let isa_flags = aarch64_settings::Flags::new(&shared_flags, builder); let backend = AArch64Backend::new_with_flags(triple, shared_flags, isa_flags); - Ok(Box::new(backend)) + Ok(backend.wrapped()) }, } } diff --git a/cranelift/codegen/src/isa/mod.rs b/cranelift/codegen/src/isa/mod.rs index 15f8aa0882..ec8edd1865 100644 --- a/cranelift/codegen/src/isa/mod.rs +++ b/cranelift/codegen/src/isa/mod.rs @@ -53,7 +53,7 @@ use crate::machinst::{CompiledCode, CompiledCodeStencil, TextSectionBuilder, Unw use crate::settings; use crate::settings::SetResult; use crate::CodegenResult; -use alloc::{boxed::Box, vec::Vec}; +use alloc::{boxed::Box, sync::Arc, vec::Vec}; use core::fmt; use core::fmt::{Debug, Formatter}; use target_lexicon::{triple, Architecture, PointerWidth, Triple}; @@ -142,14 +142,16 @@ impl fmt::Display for LookupError { } } +/// The type of a polymorphic TargetISA object which is 'static. +pub type OwnedTargetIsa = Arc; + /// Builder for a `TargetIsa`. /// Modify the ISA-specific settings before creating the `TargetIsa` trait object with `finish`. #[derive(Clone)] pub struct Builder { triple: Triple, setup: settings::Builder, - constructor: - fn(Triple, settings::Flags, settings::Builder) -> CodegenResult>, + constructor: fn(Triple, settings::Flags, settings::Builder) -> CodegenResult, } impl Builder { @@ -169,7 +171,7 @@ impl Builder { /// flags are inconsistent or incompatible: for example, some /// platform-independent features, like general SIMD support, may /// need certain ISA extensions to be enabled. - pub fn finish(self, shared_flags: settings::Flags) -> CodegenResult> { + pub fn finish(self, shared_flags: settings::Flags) -> CodegenResult { (self.constructor)(self.triple, shared_flags, self.setup) } } @@ -297,6 +299,14 @@ pub trait TargetIsa: fmt::Display + Send + Sync { /// The function alignment required by this ISA. fn function_alignment(&self) -> u32; + + /// Create a polymorphic TargetIsa from this specific implementation. + fn wrapped(self) -> OwnedTargetIsa + where + Self: Sized + 'static, + { + Arc::new(self) + } } /// Methods implemented for free for target ISA! diff --git a/cranelift/codegen/src/isa/riscv64/mod.rs b/cranelift/codegen/src/isa/riscv64/mod.rs index 07ca657c74..2880425203 100644 --- a/cranelift/codegen/src/isa/riscv64/mod.rs +++ b/cranelift/codegen/src/isa/riscv64/mod.rs @@ -193,7 +193,7 @@ pub fn isa_builder(triple: Triple) -> IsaBuilder { constructor: |triple, shared_flags, builder| { let isa_flags = riscv_settings::Flags::new(&shared_flags, builder); let backend = Riscv64Backend::new_with_flags(triple, shared_flags, isa_flags); - Ok(Box::new(backend)) + Ok(backend.wrapped()) }, } } diff --git a/cranelift/codegen/src/isa/s390x/mod.rs b/cranelift/codegen/src/isa/s390x/mod.rs index cc45c5c012..c07a597a43 100644 --- a/cranelift/codegen/src/isa/s390x/mod.rs +++ b/cranelift/codegen/src/isa/s390x/mod.rs @@ -191,7 +191,7 @@ pub fn isa_builder(triple: Triple) -> IsaBuilder { constructor: |triple, shared_flags, builder| { let isa_flags = s390x_settings::Flags::new(&shared_flags, builder); let backend = S390xBackend::new_with_flags(triple, shared_flags, isa_flags); - Ok(Box::new(backend)) + Ok(backend.wrapped()) }, } } diff --git a/cranelift/codegen/src/isa/x64/mod.rs b/cranelift/codegen/src/isa/x64/mod.rs index fae8a90b52..30483df67c 100644 --- a/cranelift/codegen/src/isa/x64/mod.rs +++ b/cranelift/codegen/src/isa/x64/mod.rs @@ -2,7 +2,7 @@ pub use self::inst::{args, EmitInfo, EmitState, Inst}; -use super::TargetIsa; +use super::{OwnedTargetIsa, TargetIsa}; use crate::ir::{condcodes::IntCC, Function, Type}; #[cfg(feature = "unwind")] use crate::isa::unwind::systemv; @@ -196,7 +196,7 @@ fn isa_constructor( triple: Triple, shared_flags: Flags, builder: shared_settings::Builder, -) -> CodegenResult> { +) -> CodegenResult { let isa_flags = x64_settings::Flags::new(&shared_flags, builder); // Check for compatibility between flags and ISA level @@ -214,7 +214,7 @@ fn isa_constructor( } let backend = X64Backend::new_with_flags(triple, shared_flags, isa_flags); - Ok(Box::new(backend)) + Ok(backend.wrapped()) } #[cfg(test)] diff --git a/cranelift/filetests/src/function_runner.rs b/cranelift/filetests/src/function_runner.rs index 39f6079050..8df5814ef5 100644 --- a/cranelift/filetests/src/function_runner.rs +++ b/cranelift/filetests/src/function_runner.rs @@ -5,7 +5,7 @@ use cranelift_codegen::data_value::DataValue; use cranelift_codegen::ir::{ ExternalName, Function, InstBuilder, Signature, UserExternalName, UserFuncName, }; -use cranelift_codegen::isa::TargetIsa; +use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa}; use cranelift_codegen::{ir, settings, CodegenError, Context}; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_jit::{JITBuilder, JITModule}; @@ -86,7 +86,7 @@ impl TestFileCompiler { /// Build a [TestFileCompiler] from a [TargetIsa]. For functions to be runnable on the /// host machine, this [TargetIsa] must match the host machine's ISA (see /// [TestFileCompiler::with_host_isa]). - pub fn new(isa: Box) -> Self { + pub fn new(isa: OwnedTargetIsa) -> Self { let builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); let module = JITModule::new(builder); let ctx = module.make_context(); diff --git a/cranelift/filetests/src/test_run.rs b/cranelift/filetests/src/test_run.rs index febc30155a..8b80ae99ef 100644 --- a/cranelift/filetests/src/test_run.rs +++ b/cranelift/filetests/src/test_run.rs @@ -8,7 +8,7 @@ use crate::subtest::{Context, SubTest}; use anyhow::Context as _; use cranelift_codegen::data_value::DataValue; use cranelift_codegen::ir::Type; -use cranelift_codegen::isa::TargetIsa; +use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa}; use cranelift_codegen::settings::{Configurable, Flags}; use cranelift_codegen::{ir, settings}; use cranelift_reader::TestCommand; @@ -34,7 +34,7 @@ fn build_host_isa( infer_native_flags: bool, flags: settings::Flags, isa_flags: Vec, -) -> Box { +) -> OwnedTargetIsa { let mut builder = cranelift_native::builder_with_options(infer_native_flags) .expect("Unable to build a TargetIsa for the current host"); diff --git a/cranelift/fuzzgen/src/lib.rs b/cranelift/fuzzgen/src/lib.rs index 97d1cd138e..e5048f79e0 100644 --- a/cranelift/fuzzgen/src/lib.rs +++ b/cranelift/fuzzgen/src/lib.rs @@ -49,7 +49,7 @@ fn write_non_default_flags(f: &mut fmt::Formatter<'_>, flags: &settings::Flags) /// A generated function with an ISA that targets one of cranelift's backends. pub struct FunctionWithIsa { /// TargetIsa to use when compiling this test case - pub isa: Box, + pub isa: isa::OwnedTargetIsa, /// Function under test pub func: Function, @@ -96,7 +96,7 @@ impl<'a> Arbitrary<'a> for FunctionWithIsa { pub struct TestCase { /// TargetIsa to use when compiling this test case - pub isa: Box, + pub isa: isa::OwnedTargetIsa, /// Function under test pub func: Function, /// Generate multiple test inputs for each test case. diff --git a/cranelift/jit/src/backend.rs b/cranelift/jit/src/backend.rs index 202dfe516a..5c2aeab817 100644 --- a/cranelift/jit/src/backend.rs +++ b/cranelift/jit/src/backend.rs @@ -1,7 +1,7 @@ //! Defines `JITModule`. use crate::{compiled_blob::CompiledBlob, memory::BranchProtection, memory::Memory}; -use cranelift_codegen::isa::TargetIsa; +use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa}; use cranelift_codegen::settings::Configurable; use cranelift_codegen::{self, ir, settings, MachReloc}; use cranelift_codegen::{binemit::Reloc, CodegenError}; @@ -26,7 +26,7 @@ const READONLY_DATA_ALIGNMENT: u64 = 0x1; /// A builder for `JITModule`. pub struct JITBuilder { - isa: Box, + isa: OwnedTargetIsa, symbols: HashMap, lookup_symbols: Vec Option<*const u8>>>, libcall_names: Box String + Send + Sync>, @@ -67,7 +67,7 @@ impl JITBuilder { /// floating point instructions, and for stack probes. If you don't know what to use for this /// argument, use `cranelift_module::default_libcall_names()`. pub fn with_isa( - isa: Box, + isa: OwnedTargetIsa, libcall_names: Box String + Send + Sync>, ) -> Self { let symbols = HashMap::new(); @@ -153,7 +153,7 @@ struct GotUpdate { /// /// See the `JITBuilder` for a convenient way to construct `JITModule` instances. pub struct JITModule { - isa: Box, + isa: OwnedTargetIsa, hotswap_enabled: bool, symbols: RefCell>, lookup_symbols: Vec Option<*const u8>>>, diff --git a/cranelift/object/src/backend.rs b/cranelift/object/src/backend.rs index f40a758221..31b16b85b4 100644 --- a/cranelift/object/src/backend.rs +++ b/cranelift/object/src/backend.rs @@ -2,7 +2,7 @@ use anyhow::anyhow; use cranelift_codegen::entity::SecondaryMap; -use cranelift_codegen::isa::TargetIsa; +use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa}; use cranelift_codegen::{self, ir, MachReloc}; use cranelift_codegen::{ binemit::{Addend, CodeOffset, Reloc}, @@ -26,7 +26,7 @@ use target_lexicon::PointerWidth; /// A builder for `ObjectModule`. pub struct ObjectBuilder { - isa: Box, + isa: OwnedTargetIsa, binary_format: object::BinaryFormat, architecture: object::Architecture, flags: object::FileFlags, @@ -45,7 +45,7 @@ impl ObjectBuilder { /// floating point instructions, and for stack probes. If you don't know what to use for this /// argument, use [cranelift_module::default_libcall_names](). pub fn new>>( - isa: Box, + isa: OwnedTargetIsa, name: V, libcall_names: Box String + Send + Sync>, ) -> ModuleResult { @@ -124,7 +124,7 @@ impl ObjectBuilder { /// /// See the `ObjectBuilder` for a convenient way to construct `ObjectModule` instances. pub struct ObjectModule { - isa: Box, + isa: OwnedTargetIsa, object: Object<'static>, declarations: ModuleDeclarations, functions: SecondaryMap>, diff --git a/cranelift/reader/src/isaspec.rs b/cranelift/reader/src/isaspec.rs index 6c1058c599..6d5262164d 100644 --- a/cranelift/reader/src/isaspec.rs +++ b/cranelift/reader/src/isaspec.rs @@ -8,7 +8,7 @@ use crate::error::{Location, ParseError}; use crate::testcommand::TestOption; -use cranelift_codegen::isa::TargetIsa; +use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa}; use cranelift_codegen::settings::{Configurable, Flags, SetError}; /// The ISA specifications in a `.clif` file. @@ -19,7 +19,7 @@ pub enum IsaSpec { /// The parsed file does contain `isa` commands. /// Each `isa` command is used to configure a `TargetIsa` trait object. - Some(Vec>), + Some(Vec), } impl IsaSpec { diff --git a/cranelift/reader/src/lib.rs b/cranelift/reader/src/lib.rs index b627d69b19..a691970bca 100644 --- a/cranelift/reader/src/lib.rs +++ b/cranelift/reader/src/lib.rs @@ -43,7 +43,7 @@ mod testcommand; mod testfile; use anyhow::{Error, Result}; -use cranelift_codegen::isa::{self, TargetIsa}; +use cranelift_codegen::isa::{self, OwnedTargetIsa}; use cranelift_codegen::settings::{self, FlagsOrIsa}; use std::str::FromStr; use target_lexicon::Triple; @@ -52,7 +52,7 @@ use target_lexicon::Triple; #[allow(missing_docs)] pub enum OwnedFlagsOrIsa { Flags(settings::Flags), - Isa(Box), + Isa(OwnedTargetIsa), } impl OwnedFlagsOrIsa { diff --git a/cranelift/src/compile.rs b/cranelift/src/compile.rs index 8852c6aa8d..be9315c8d0 100644 --- a/cranelift/src/compile.rs +++ b/cranelift/src/compile.rs @@ -8,6 +8,7 @@ use cranelift_codegen::print_errors::pretty_error; use cranelift_codegen::settings::FlagsOrIsa; use cranelift_codegen::timing; use cranelift_codegen::Context; +use cranelift_reader::OwnedFlagsOrIsa; use cranelift_reader::{parse_sets_and_triple, parse_test, ParseOptions}; use std::path::Path; use std::path::PathBuf; @@ -37,18 +38,50 @@ pub struct Options { /// Specify an input file to be used. Use '-' for stdin. files: Vec, + + /// Output object file + #[clap(short = 'o', long = "output")] + output: Option, } pub fn run(options: &Options) -> Result<()> { let parsed = parse_sets_and_triple(&options.settings, &options.target)?; + + let mut module = match (&options.output, &parsed) { + (Some(output), OwnedFlagsOrIsa::Isa(isa)) => { + let builder = cranelift_object::ObjectBuilder::new( + isa.clone(), + output + .file_name() + .and_then(|s| s.to_str()) + .unwrap_or("a.out"), + cranelift_module::default_libcall_names(), + )?; + Some(cranelift_object::ObjectModule::new(builder)) + } + _ => None, + }; + for path in &options.files { let name = String::from(path.as_os_str().to_string_lossy()); - handle_module(options, path, &name, parsed.as_fisa())?; + handle_module(options, path, &name, parsed.as_fisa(), module.as_mut())?; } + + if let (Some(module), Some(output)) = (module, &options.output) { + let bytes = module.finish().emit()?; + std::fs::write(output, bytes)?; + } + Ok(()) } -fn handle_module(options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa) -> Result<()> { +fn handle_module( + options: &Options, + path: &Path, + name: &str, + fisa: FlagsOrIsa, + module: Option<&mut impl cranelift_module::Module>, +) -> Result<()> { let buffer = read_to_string(&path)?; let test_file = parse_test(&buffer, ParseOptions::default()) .with_context(|| format!("failed to parse {}", name))?; @@ -57,39 +90,48 @@ fn handle_module(options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa) - // file contains a unique isa, use that. let isa = fisa.isa.or(test_file.isa_spec.unique_isa()); - if isa.is_none() { - anyhow::bail!("compilation requires a target isa"); + let isa = match isa { + None => anyhow::bail!("compilation requires a target isa"), + Some(isa) => isa, }; for (func, _) in test_file.functions { - if let Some(isa) = isa { - let mut context = Context::new(); - context.func = func; - let mut mem = vec![]; + let mut context = Context::new(); + context.func = func; + let mut mem = vec![]; - // Compile and encode the result to machine code. - let compiled_code = context - .compile_and_emit(isa, &mut mem) - .map_err(|err| anyhow::anyhow!("{}", pretty_error(&err.func, err.inner)))?; - let code_info = compiled_code.code_info(); + // Compile and encode the result to machine code. + let compiled_code = context + .compile_and_emit(isa, &mut mem) + .map_err(|err| anyhow::anyhow!("{}", pretty_error(&err.func, err.inner)))?; + let code_info = compiled_code.code_info(); - if options.print { - println!("{}", context.func.display()); - } + if let Some(&mut ref mut module) = module { + let name = context.func.name.to_string(); + let fid = module.declare_function( + &name, + cranelift_module::Linkage::Export, + &context.func.signature, + )?; + module.define_function(fid, &mut context)?; + } - if options.disasm { - let result = context.compiled_code().unwrap(); - print_all( - isa, - &context.func.params, - &mem, - code_info.total_size, - options.print, - result.buffer.relocs(), - result.buffer.traps(), - result.buffer.stack_maps(), - )?; - } + if options.print { + println!("{}", context.func.display()); + } + + if options.disasm { + let result = context.compiled_code().unwrap(); + print_all( + isa, + &context.func.params, + &mem, + code_info.total_size, + options.print, + result.buffer.relocs(), + result.buffer.traps(), + result.buffer.stack_maps(), + )?; } } diff --git a/cranelift/src/run.rs b/cranelift/src/run.rs index 8a5fa2d0f6..5564736e3b 100644 --- a/cranelift/src/run.rs +++ b/cranelift/src/run.rs @@ -3,7 +3,7 @@ use crate::utils::{iterate_files, read_to_string}; use anyhow::Result; use clap::Parser; -use cranelift_codegen::isa::{CallConv, TargetIsa}; +use cranelift_codegen::isa::{CallConv, OwnedTargetIsa}; use cranelift_filetests::TestFileCompiler; use cranelift_native::builder as host_isa_builder; use cranelift_reader::{parse_run_command, parse_test, Details, IsaSpec, ParseOptions}; @@ -105,7 +105,7 @@ fn run_file_contents(file_contents: String) -> Result<()> { } /// Build an ISA based on the current machine running this code (the host) -fn create_target_isa(isa_spec: &IsaSpec) -> Result> { +fn create_target_isa(isa_spec: &IsaSpec) -> Result { if let IsaSpec::None(flags) = isa_spec { // build an ISA for the current machine let builder = host_isa_builder().map_err(|s| anyhow::anyhow!("{}", s))?; diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs index 1f3777aaf4..512a837d69 100644 --- a/crates/cranelift/src/compiler.rs +++ b/crates/cranelift/src/compiler.rs @@ -10,7 +10,7 @@ use anyhow::{Context as _, Result}; use cranelift_codegen::ir::{ self, ExternalName, Function, InstBuilder, MemFlags, UserExternalName, UserFuncName, Value, }; -use cranelift_codegen::isa::TargetIsa; +use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa}; use cranelift_codegen::print_errors::pretty_error; use cranelift_codegen::Context; use cranelift_codegen::{settings, MachReloc, MachTrap}; @@ -68,7 +68,7 @@ impl Default for CompilerContext { /// the Wasm to Compiler IR, optimizing it and then translating to assembly. pub(crate) struct Compiler { contexts: Mutex>, - isa: Box, + isa: OwnedTargetIsa, linkopts: LinkOptions, cache_store: Option>, } @@ -103,7 +103,7 @@ impl Drop for Compiler { impl Compiler { pub(crate) fn new( - isa: Box, + isa: OwnedTargetIsa, cache_store: Option>, linkopts: LinkOptions, ) -> Compiler {