Improvements to Modules API (#307)
* test-no_std: use cargo +nightly assume folks have rustup set to use stable by default * cretonne-module, -faerie, -simplejit: use new ModuleError enum CtonError is not really appropriate for use in the module system. Instead, create a new enum ModuleError, which implements failure::Fail (works with no_std). Translate existing panics and unimplemented error cases to return ModuleErrors. * cretonne-faerie: export FaerieProduct * cretonne-module: expose FuncOrDataId, and Module::get_name to lookup This is helpful for looking up a name that has already been declared. Also, implement FuncOrDataId -> ExternalName conversion. * cretonne-faerie: depend on faerie 0.3.0 which has bugfix for data relocations * cretonne-module: change InvalidDefinition to InvalidImportDefinition per dan's code review. plus another typo fix * cretonne-faerie: add optional manifest of all traps from codegen * cretonne-module: provide more context in panics * cretonne-faerie: updates to docs * cretonne-faerie: return an Err instead of debug_assert when isa not pic
This commit is contained in:
@@ -21,10 +21,10 @@ do
|
|||||||
cd "lib/$LIB"
|
cd "lib/$LIB"
|
||||||
|
|
||||||
# Test with just "core" enabled.
|
# Test with just "core" enabled.
|
||||||
cargo test --no-default-features --features core
|
cargo +nightly test --no-default-features --features core
|
||||||
|
|
||||||
# Test with "core" and "std" enabled at the same time.
|
# Test with "core" and "std" enabled at the same time.
|
||||||
cargo test --features core
|
cargo +nightly test --features core
|
||||||
|
|
||||||
cd "$topdir"
|
cd "$topdir"
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ readme = "README.md"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
cretonne-codegen = { path = "../codegen", version = "0.6.0" }
|
cretonne-codegen = { path = "../codegen", version = "0.6.0" }
|
||||||
cretonne-module = { path = "../module", version = "0.6.0" }
|
cretonne-module = { path = "../module", version = "0.6.0" }
|
||||||
faerie = "0.2.0"
|
faerie = "0.3.0"
|
||||||
goblin = "0.0.14"
|
goblin = "0.0.14"
|
||||||
failure = "0.1.1"
|
failure = "0.1.1"
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,25 @@
|
|||||||
use container;
|
use container;
|
||||||
use cretonne_codegen::binemit::{Addend, CodeOffset, Reloc, RelocSink, NullTrapSink};
|
use cretonne_codegen::binemit::{Addend, CodeOffset, Reloc, RelocSink, NullTrapSink};
|
||||||
use cretonne_codegen::isa::TargetIsa;
|
use cretonne_codegen::isa::TargetIsa;
|
||||||
use cretonne_codegen::result::CtonError;
|
|
||||||
use cretonne_codegen::{self, binemit, ir};
|
use cretonne_codegen::{self, binemit, ir};
|
||||||
use cretonne_module::{Backend, DataContext, Linkage, ModuleNamespace, Init, DataDescription};
|
use cretonne_module::{Backend, DataContext, Linkage, ModuleNamespace, Init, DataDescription,
|
||||||
use faerie;
|
ModuleError};
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
|
use faerie;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use target;
|
use target;
|
||||||
|
use traps::{FaerieTrapManifest, FaerieTrapSink};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
/// Setting to enable collection of traps. Setting this to `Enabled` in
|
||||||
|
/// `FaerieBuilder` means that a `FaerieTrapManifest` will be present
|
||||||
|
/// in the `FaerieProduct`.
|
||||||
|
pub enum FaerieTrapCollection {
|
||||||
|
/// `FaerieProduct::trap_manifest` will be `None`
|
||||||
|
Disabled,
|
||||||
|
/// `FaerieProduct::trap_manifest` will be `Some`
|
||||||
|
Enabled,
|
||||||
|
}
|
||||||
|
|
||||||
/// A builder for `FaerieBackend`.
|
/// A builder for `FaerieBackend`.
|
||||||
pub struct FaerieBuilder {
|
pub struct FaerieBuilder {
|
||||||
@@ -17,6 +29,7 @@ pub struct FaerieBuilder {
|
|||||||
name: String,
|
name: String,
|
||||||
format: container::Format,
|
format: container::Format,
|
||||||
faerie_target: faerie::Target,
|
faerie_target: faerie::Target,
|
||||||
|
collect_traps: FaerieTrapCollection,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FaerieBuilder {
|
impl FaerieBuilder {
|
||||||
@@ -24,21 +37,28 @@ impl FaerieBuilder {
|
|||||||
/// can be passed to
|
/// can be passed to
|
||||||
/// [`Module::new`](cretonne_module/struct.Module.html#method.new].
|
/// [`Module::new`](cretonne_module/struct.Module.html#method.new].
|
||||||
///
|
///
|
||||||
/// Note: To support calls JIT'd functions from Rust or other compiled
|
/// Faerie output requires that TargetIsa have PIC (Position Independent Code) enabled.
|
||||||
/// code, it's necessary for the `call_conv` setting in `isa`'s flags
|
///
|
||||||
/// to match the host platform.
|
/// `collect_traps` setting determines whether trap information is collected in a
|
||||||
|
/// `FaerieTrapManifest` available in the `FaerieProduct`.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
isa: Box<TargetIsa>,
|
isa: Box<TargetIsa>,
|
||||||
name: String,
|
name: String,
|
||||||
format: container::Format,
|
format: container::Format,
|
||||||
) -> Result<Self, Error> {
|
collect_traps: FaerieTrapCollection,
|
||||||
debug_assert!(isa.flags().is_pic(), "faerie requires PIC");
|
) -> Result<Self, ModuleError> {
|
||||||
|
if !isa.flags().is_pic() {
|
||||||
|
return Err(ModuleError::Backend(
|
||||||
|
"faerie requires TargetIsa be PIC".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
let faerie_target = target::translate(&*isa)?;
|
let faerie_target = target::translate(&*isa)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
isa,
|
isa,
|
||||||
name,
|
name,
|
||||||
format,
|
format,
|
||||||
faerie_target,
|
faerie_target,
|
||||||
|
collect_traps,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,6 +68,7 @@ pub struct FaerieBackend {
|
|||||||
isa: Box<TargetIsa>,
|
isa: Box<TargetIsa>,
|
||||||
artifact: faerie::Artifact,
|
artifact: faerie::Artifact,
|
||||||
format: container::Format,
|
format: container::Format,
|
||||||
|
trap_manifest: Option<FaerieTrapManifest>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FaerieCompiledFunction {}
|
pub struct FaerieCompiledFunction {}
|
||||||
@@ -75,6 +96,10 @@ impl Backend for FaerieBackend {
|
|||||||
isa: builder.isa,
|
isa: builder.isa,
|
||||||
artifact: faerie::Artifact::new(builder.faerie_target, builder.name),
|
artifact: faerie::Artifact::new(builder.faerie_target, builder.name),
|
||||||
format: builder.format,
|
format: builder.format,
|
||||||
|
trap_manifest: match builder.collect_traps {
|
||||||
|
FaerieTrapCollection::Enabled => Some(FaerieTrapManifest::new()),
|
||||||
|
FaerieTrapCollection::Disabled => None,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +125,7 @@ impl Backend for FaerieBackend {
|
|||||||
ctx: &cretonne_codegen::Context,
|
ctx: &cretonne_codegen::Context,
|
||||||
namespace: &ModuleNamespace<Self>,
|
namespace: &ModuleNamespace<Self>,
|
||||||
code_size: u32,
|
code_size: u32,
|
||||||
) -> Result<FaerieCompiledFunction, CtonError> {
|
) -> Result<FaerieCompiledFunction, ModuleError> {
|
||||||
let mut code: Vec<u8> = Vec::with_capacity(code_size as usize);
|
let mut code: Vec<u8> = Vec::with_capacity(code_size as usize);
|
||||||
code.resize(code_size as usize, 0);
|
code.resize(code_size as usize, 0);
|
||||||
|
|
||||||
@@ -112,18 +137,29 @@ impl Backend for FaerieBackend {
|
|||||||
name,
|
name,
|
||||||
namespace,
|
namespace,
|
||||||
};
|
};
|
||||||
// Ignore traps for now. For now, frontends should just avoid generating code
|
|
||||||
// that traps.
|
|
||||||
let mut trap_sink = NullTrapSink {};
|
|
||||||
|
|
||||||
unsafe {
|
if let Some(ref mut trap_manifest) = self.trap_manifest {
|
||||||
ctx.emit_to_memory(
|
let mut trap_sink = FaerieTrapSink::new(name, code_size);
|
||||||
&*self.isa,
|
unsafe {
|
||||||
code.as_mut_ptr(),
|
ctx.emit_to_memory(
|
||||||
&mut reloc_sink,
|
&*self.isa,
|
||||||
&mut trap_sink,
|
code.as_mut_ptr(),
|
||||||
)
|
&mut reloc_sink,
|
||||||
};
|
&mut trap_sink,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
trap_manifest.add_sink(trap_sink);
|
||||||
|
} else {
|
||||||
|
let mut trap_sink = NullTrapSink {};
|
||||||
|
unsafe {
|
||||||
|
ctx.emit_to_memory(
|
||||||
|
&*self.isa,
|
||||||
|
code.as_mut_ptr(),
|
||||||
|
&mut reloc_sink,
|
||||||
|
&mut trap_sink,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.artifact.define(name, code).expect(
|
self.artifact.define(name, code).expect(
|
||||||
@@ -137,7 +173,7 @@ impl Backend for FaerieBackend {
|
|||||||
name: &str,
|
name: &str,
|
||||||
data_ctx: &DataContext,
|
data_ctx: &DataContext,
|
||||||
namespace: &ModuleNamespace<Self>,
|
namespace: &ModuleNamespace<Self>,
|
||||||
) -> Result<FaerieCompiledData, CtonError> {
|
) -> Result<FaerieCompiledData, ModuleError> {
|
||||||
let &DataDescription {
|
let &DataDescription {
|
||||||
writable: _writable,
|
writable: _writable,
|
||||||
ref init,
|
ref init,
|
||||||
@@ -161,8 +197,6 @@ impl Backend for FaerieBackend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Change the signature of this function to use something other
|
|
||||||
// than `CtonError`, as `CtonError` can't convey faerie's errors.
|
|
||||||
for &(offset, id) in function_relocs {
|
for &(offset, id) in function_relocs {
|
||||||
let to = &namespace.get_function_decl(&function_decls[id]).name;
|
let to = &namespace.get_function_decl(&function_decls[id]).name;
|
||||||
self.artifact
|
self.artifact
|
||||||
@@ -171,7 +205,7 @@ impl Backend for FaerieBackend {
|
|||||||
to,
|
to,
|
||||||
at: offset as usize,
|
at: offset as usize,
|
||||||
})
|
})
|
||||||
.map_err(|_e| CtonError::InvalidInput)?;
|
.map_err(|e| ModuleError::Backend(format!("{}", e)))?;
|
||||||
}
|
}
|
||||||
for &(offset, id, addend) in data_relocs {
|
for &(offset, id, addend) in data_relocs {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
@@ -186,7 +220,7 @@ impl Backend for FaerieBackend {
|
|||||||
to,
|
to,
|
||||||
at: offset as usize,
|
at: offset as usize,
|
||||||
})
|
})
|
||||||
.map_err(|_e| CtonError::InvalidInput)?;
|
.map_err(|e| ModuleError::Backend(format!("{}", e)))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.artifact.define(name, bytes).expect(
|
self.artifact.define(name, bytes).expect(
|
||||||
@@ -230,6 +264,7 @@ impl Backend for FaerieBackend {
|
|||||||
FaerieProduct {
|
FaerieProduct {
|
||||||
artifact: self.artifact,
|
artifact: self.artifact,
|
||||||
format: self.format,
|
format: self.format,
|
||||||
|
trap_manifest: self.trap_manifest,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,7 +273,12 @@ impl Backend for FaerieBackend {
|
|||||||
/// [`finish`](../cretonne_module/struct.Module.html#method.finish) function.
|
/// [`finish`](../cretonne_module/struct.Module.html#method.finish) function.
|
||||||
/// It provides functions for writing out the object file to memory or a file.
|
/// It provides functions for writing out the object file to memory or a file.
|
||||||
pub struct FaerieProduct {
|
pub struct FaerieProduct {
|
||||||
artifact: faerie::Artifact,
|
/// Faerie artifact with all functions, data, and links from the module defined
|
||||||
|
pub artifact: faerie::Artifact,
|
||||||
|
/// Optional trap manifest. Contains `FaerieTrapManifest` when `FaerieBuilder.collect_traps` is
|
||||||
|
/// set to `FaerieTrapCollection::Enabled`.
|
||||||
|
pub trap_manifest: Option<FaerieTrapManifest>,
|
||||||
|
/// The format that the builder specified for output.
|
||||||
format: container::Format,
|
format: container::Format,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,13 +21,13 @@
|
|||||||
extern crate cretonne_codegen;
|
extern crate cretonne_codegen;
|
||||||
extern crate cretonne_module;
|
extern crate cretonne_module;
|
||||||
extern crate faerie;
|
extern crate faerie;
|
||||||
#[macro_use]
|
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
extern crate goblin;
|
extern crate goblin;
|
||||||
|
|
||||||
mod backend;
|
mod backend;
|
||||||
mod container;
|
mod container;
|
||||||
mod target;
|
mod target;
|
||||||
|
pub mod traps;
|
||||||
|
|
||||||
pub use backend::{FaerieBuilder, FaerieBackend};
|
pub use backend::{FaerieBuilder, FaerieBackend, FaerieProduct, FaerieTrapCollection};
|
||||||
pub use container::Format;
|
pub use container::Format;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use cretonne_codegen::isa;
|
use cretonne_codegen::isa;
|
||||||
|
use cretonne_module::ModuleError;
|
||||||
use faerie::Target;
|
use faerie::Target;
|
||||||
use failure::Error;
|
|
||||||
|
|
||||||
/// Translate from a Cretonne `TargetIsa` to a Faerie `Target`.
|
/// Translate from a Cretonne `TargetIsa` to a Faerie `Target`.
|
||||||
pub fn translate(isa: &isa::TargetIsa) -> Result<Target, Error> {
|
pub fn translate(isa: &isa::TargetIsa) -> Result<Target, ModuleError> {
|
||||||
let name = isa.name();
|
let name = isa.name();
|
||||||
match name {
|
match name {
|
||||||
"x86" => Ok(if isa.flags().is_64bit() {
|
"x86" => Ok(if isa.flags().is_64bit() {
|
||||||
@@ -13,6 +13,8 @@ pub fn translate(isa: &isa::TargetIsa) -> Result<Target, Error> {
|
|||||||
}),
|
}),
|
||||||
"arm32" => Ok(Target::ARMv7),
|
"arm32" => Ok(Target::ARMv7),
|
||||||
"arm64" => Ok(Target::ARM64),
|
"arm64" => Ok(Target::ARM64),
|
||||||
_ => Err(format_err!("unsupported isa: {}", name)),
|
_ => Err(ModuleError::Backend(
|
||||||
|
format!("unsupported faerie isa: {}", name),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
64
lib/faerie/src/traps.rs
Normal file
64
lib/faerie/src/traps.rs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
//! Faerie trap manifests record every `TrapCode` that cretonne outputs during code generation,
|
||||||
|
//! for every function in the module. This data may be useful at runtime.
|
||||||
|
|
||||||
|
use cretonne_codegen::{ir, binemit};
|
||||||
|
|
||||||
|
/// Record of the arguments cretonne passes to `TrapSink::trap`
|
||||||
|
pub struct FaerieTrapSite {
|
||||||
|
/// Offset into function
|
||||||
|
pub offset: binemit::CodeOffset,
|
||||||
|
/// Source location given to cretonne
|
||||||
|
pub srcloc: ir::SourceLoc,
|
||||||
|
/// Trap code, as determined by cretonne
|
||||||
|
pub code: ir::TrapCode,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Record of the trap sites for a given function
|
||||||
|
pub struct FaerieTrapSink {
|
||||||
|
/// Name of function
|
||||||
|
pub name: String,
|
||||||
|
/// Total code size of function
|
||||||
|
pub code_size: u32,
|
||||||
|
/// All trap sites collected in function
|
||||||
|
pub sites: Vec<FaerieTrapSite>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl FaerieTrapSink {
|
||||||
|
/// Create an empty `FaerieTrapSink`
|
||||||
|
pub fn new(name: &str, code_size: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
sites: Vec::new(),
|
||||||
|
name: name.to_owned(),
|
||||||
|
code_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl binemit::TrapSink for FaerieTrapSink {
|
||||||
|
fn trap(&mut self, offset: binemit::CodeOffset, srcloc: ir::SourceLoc, code: ir::TrapCode) {
|
||||||
|
self.sites.push(FaerieTrapSite {
|
||||||
|
offset,
|
||||||
|
srcloc,
|
||||||
|
code,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collection of all `FaerieTrapSink`s for the module
|
||||||
|
pub struct FaerieTrapManifest {
|
||||||
|
/// All `FaerieTrapSink` for the module
|
||||||
|
pub sinks: Vec<FaerieTrapSink>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FaerieTrapManifest {
|
||||||
|
/// Create an empty `FaerieTrapManifest`
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { sinks: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Put a `FaerieTrapSink` into manifest
|
||||||
|
pub fn add_sink(&mut self, sink: FaerieTrapSink) {
|
||||||
|
self.sinks.push(sink);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ readme = "README.md"
|
|||||||
cretonne-codegen = { path = "../codegen", version = "0.6.0", default-features = false }
|
cretonne-codegen = { path = "../codegen", version = "0.6.0", default-features = false }
|
||||||
cretonne-entity = { path = "../entity", version = "0.6.0", default-features = false }
|
cretonne-entity = { path = "../entity", version = "0.6.0", default-features = false }
|
||||||
hashmap_core = { version = "0.1.4", optional = true }
|
hashmap_core = { version = "0.1.4", optional = true }
|
||||||
|
failure = "0.1.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
use DataContext;
|
use DataContext;
|
||||||
use Linkage;
|
use Linkage;
|
||||||
use ModuleNamespace;
|
use ModuleNamespace;
|
||||||
|
use ModuleError;
|
||||||
use cretonne_codegen::Context;
|
use cretonne_codegen::Context;
|
||||||
use cretonne_codegen::isa::TargetIsa;
|
use cretonne_codegen::isa::TargetIsa;
|
||||||
use cretonne_codegen::result::CtonError;
|
|
||||||
use cretonne_codegen::{binemit, ir};
|
use cretonne_codegen::{binemit, ir};
|
||||||
use std::marker;
|
use std::marker;
|
||||||
|
|
||||||
@@ -57,19 +57,17 @@ where
|
|||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
namespace: &ModuleNamespace<Self>,
|
namespace: &ModuleNamespace<Self>,
|
||||||
code_size: u32,
|
code_size: u32,
|
||||||
) -> Result<Self::CompiledFunction, CtonError>;
|
) -> Result<Self::CompiledFunction, ModuleError>;
|
||||||
|
|
||||||
/// Define a zero-initialized data object of the given size.
|
/// Define a zero-initialized data object of the given size.
|
||||||
///
|
///
|
||||||
/// Data objects must be declared before being defined.
|
/// Data objects must be declared before being defined.
|
||||||
///
|
|
||||||
/// TODO: Is CtonError the right error code here?
|
|
||||||
fn define_data(
|
fn define_data(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
data_ctx: &DataContext,
|
data_ctx: &DataContext,
|
||||||
namespace: &ModuleNamespace<Self>,
|
namespace: &ModuleNamespace<Self>,
|
||||||
) -> Result<Self::CompiledData, CtonError>;
|
) -> Result<Self::CompiledData, ModuleError>;
|
||||||
|
|
||||||
/// Write the address of `what` into the data for `data` at `offset`. `data` must refer to a
|
/// Write the address of `what` into the data for `data` at `offset`. `data` must refer to a
|
||||||
/// defined data object.
|
/// defined data object.
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ impl DataContext {
|
|||||||
self.description.init = Init::Zeros { size };
|
self.description.init = Init::Zeros { size };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Define a zero-initialized object with the given size.
|
/// Define an object initialized with the given contents.
|
||||||
///
|
///
|
||||||
/// TODO: Can we avoid a Box here?
|
/// TODO: Can we avoid a Box here?
|
||||||
pub fn define(&mut self, contents: Box<[u8]>, writable: Writability) {
|
pub fn define(&mut self, contents: Box<[u8]>, writable: Writability) {
|
||||||
|
|||||||
@@ -16,9 +16,12 @@
|
|||||||
use_self,
|
use_self,
|
||||||
))]
|
))]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
extern crate cretonne_codegen;
|
extern crate cretonne_codegen;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate cretonne_entity;
|
extern crate cretonne_entity;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate failure;
|
||||||
|
|
||||||
mod backend;
|
mod backend;
|
||||||
mod data_context;
|
mod data_context;
|
||||||
@@ -26,4 +29,4 @@ mod module;
|
|||||||
|
|
||||||
pub use backend::Backend;
|
pub use backend::Backend;
|
||||||
pub use data_context::{DataContext, Writability, DataDescription, Init};
|
pub use data_context::{DataContext, Writability, DataDescription, Init};
|
||||||
pub use module::{DataId, FuncId, Linkage, Module, ModuleNamespace};
|
pub use module::{DataId, FuncId, FuncOrDataId, Linkage, Module, ModuleNamespace, ModuleError};
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
use Backend;
|
use Backend;
|
||||||
use cretonne_codegen::entity::{EntityRef, PrimaryMap};
|
use cretonne_codegen::entity::{EntityRef, PrimaryMap};
|
||||||
use cretonne_codegen::result::{CtonError, CtonResult};
|
use cretonne_codegen::result::CtonError;
|
||||||
use cretonne_codegen::{binemit, ir, Context};
|
use cretonne_codegen::{binemit, ir, Context};
|
||||||
use data_context::DataContext;
|
use data_context::DataContext;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -17,11 +17,32 @@ use std::collections::HashMap;
|
|||||||
pub struct FuncId(u32);
|
pub struct FuncId(u32);
|
||||||
entity_impl!(FuncId, "funcid");
|
entity_impl!(FuncId, "funcid");
|
||||||
|
|
||||||
|
/// Function identifiers are namespace 0 in `ir::ExternalName`
|
||||||
|
impl From<FuncId> for ir::ExternalName {
|
||||||
|
fn from(id: FuncId) -> ir::ExternalName {
|
||||||
|
ir::ExternalName::User {
|
||||||
|
namespace: 0,
|
||||||
|
index: id.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A data object identifier for use in the `Module` interface.
|
/// A data object identifier for use in the `Module` interface.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub struct DataId(u32);
|
pub struct DataId(u32);
|
||||||
entity_impl!(DataId, "dataid");
|
entity_impl!(DataId, "dataid");
|
||||||
|
|
||||||
|
/// Data identifiers are namespace 1 in `ir::ExternalName`
|
||||||
|
impl From<DataId> for ir::ExternalName {
|
||||||
|
fn from(id: DataId) -> ir::ExternalName {
|
||||||
|
ir::ExternalName::User {
|
||||||
|
namespace: 1,
|
||||||
|
index: id.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Linkage refers to where an entity is defined and who can see it.
|
/// Linkage refers to where an entity is defined and who can see it.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum Linkage {
|
pub enum Linkage {
|
||||||
@@ -73,12 +94,26 @@ impl Linkage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
|
||||||
enum FuncOrDataId {
|
/// A declared name may refer to either a function or data declaration
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||||
|
pub enum FuncOrDataId {
|
||||||
|
/// When it's a FuncId
|
||||||
Func(FuncId),
|
Func(FuncId),
|
||||||
|
/// When it's a DataId
|
||||||
Data(DataId),
|
Data(DataId),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mapping to `ir::ExternalName` is trivial based on the `FuncId` and `DataId` mapping.
|
||||||
|
impl From<FuncOrDataId> for ir::ExternalName {
|
||||||
|
fn from(id: FuncOrDataId) -> ir::ExternalName {
|
||||||
|
match id {
|
||||||
|
FuncOrDataId::Func(funcid) => ir::ExternalName::from(funcid),
|
||||||
|
FuncOrDataId::Data(dataid) => ir::ExternalName::from(dataid),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Information about a function which can be called.
|
/// Information about a function which can be called.
|
||||||
pub struct FunctionDeclaration {
|
pub struct FunctionDeclaration {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@@ -86,6 +121,29 @@ pub struct FunctionDeclaration {
|
|||||||
pub signature: ir::Signature,
|
pub signature: ir::Signature,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error messages for all `Module` and `Backend` methods
|
||||||
|
#[derive(Fail, Debug)]
|
||||||
|
pub enum ModuleError {
|
||||||
|
/// Indicates an identifier was used before it was declared
|
||||||
|
#[fail(display = "Undeclared identifier: {}", _0)]
|
||||||
|
Undeclared(String),
|
||||||
|
/// Indicates an identifier was used contrary to the way it was declared
|
||||||
|
#[fail(display = "Incompatible declaration of identifier: {}", _0)]
|
||||||
|
IncompatibleDeclaration(String),
|
||||||
|
/// Indicates an identifier was defined more than once
|
||||||
|
#[fail(display = "Duplicate definition of identifier: {}", _0)]
|
||||||
|
DuplicateDefinition(String),
|
||||||
|
/// Indicates an identifier was defined, but was declared as an import
|
||||||
|
#[fail(display = "Invalid to define identifier declared as an import: {}", _0)]
|
||||||
|
InvalidImportDefinition(String),
|
||||||
|
/// Wraps a `cretonne-codegen` error
|
||||||
|
#[fail(display = "Compilation error: {}", _0)]
|
||||||
|
Compilation(CtonError),
|
||||||
|
/// Wraps a generic error from a backend
|
||||||
|
#[fail(display = "Backend error: {}", _0)]
|
||||||
|
Backend(String),
|
||||||
|
}
|
||||||
|
|
||||||
/// A function belonging to a `Module`.
|
/// A function belonging to a `Module`.
|
||||||
struct ModuleFunction<B>
|
struct ModuleFunction<B>
|
||||||
where
|
where
|
||||||
@@ -157,7 +215,7 @@ where
|
|||||||
let func = FuncId::new(index as usize);
|
let func = FuncId::new(index as usize);
|
||||||
&self.functions[func]
|
&self.functions[func]
|
||||||
} else {
|
} else {
|
||||||
panic!("unexpected ExternalName kind")
|
panic!("unexpected ExternalName kind {}", name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +226,7 @@ where
|
|||||||
let data = DataId::new(index as usize);
|
let data = DataId::new(index as usize);
|
||||||
&self.data_objects[data]
|
&self.data_objects[data]
|
||||||
} else {
|
} else {
|
||||||
panic!("unexpected ExternalName kind")
|
panic!("unexpected ExternalName kind {}", name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -227,7 +285,7 @@ where
|
|||||||
if let ir::ExternalName::User { namespace, .. } = *name {
|
if let ir::ExternalName::User { namespace, .. } = *name {
|
||||||
namespace == 0
|
namespace == 0
|
||||||
} else {
|
} else {
|
||||||
panic!("unexpected ExternalName kind")
|
panic!("unexpected ExternalName kind {}", name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,6 +316,12 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the module identifier for a given name, if that name
|
||||||
|
/// has been declared.
|
||||||
|
pub fn get_name(&self, name: &str) -> Option<FuncOrDataId> {
|
||||||
|
self.names.get(name).map(|e| *e)
|
||||||
|
}
|
||||||
|
|
||||||
/// Return then pointer type for the current target.
|
/// Return then pointer type for the current target.
|
||||||
pub fn pointer_type(&self) -> ir::types::Type {
|
pub fn pointer_type(&self) -> ir::types::Type {
|
||||||
if self.backend.isa().flags().is_64bit() {
|
if self.backend.isa().flags().is_64bit() {
|
||||||
@@ -273,7 +337,7 @@ where
|
|||||||
name: &str,
|
name: &str,
|
||||||
linkage: Linkage,
|
linkage: Linkage,
|
||||||
signature: &ir::Signature,
|
signature: &ir::Signature,
|
||||||
) -> Result<FuncId, CtonError> {
|
) -> Result<FuncId, ModuleError> {
|
||||||
// TODO: Can we avoid allocating names so often?
|
// TODO: Can we avoid allocating names so often?
|
||||||
use std::collections::hash_map::Entry::*;
|
use std::collections::hash_map::Entry::*;
|
||||||
match self.names.entry(name.to_owned()) {
|
match self.names.entry(name.to_owned()) {
|
||||||
@@ -285,7 +349,9 @@ where
|
|||||||
self.backend.declare_function(name, existing.decl.linkage);
|
self.backend.declare_function(name, existing.decl.linkage);
|
||||||
Ok(id)
|
Ok(id)
|
||||||
}
|
}
|
||||||
FuncOrDataId::Data(..) => unimplemented!(),
|
FuncOrDataId::Data(..) => Err(
|
||||||
|
ModuleError::IncompatibleDeclaration(name.to_owned()),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Vacant(entry) => {
|
Vacant(entry) => {
|
||||||
@@ -311,7 +377,7 @@ where
|
|||||||
name: &str,
|
name: &str,
|
||||||
linkage: Linkage,
|
linkage: Linkage,
|
||||||
writable: bool,
|
writable: bool,
|
||||||
) -> Result<DataId, CtonError> {
|
) -> Result<DataId, ModuleError> {
|
||||||
// TODO: Can we avoid allocating names so often?
|
// TODO: Can we avoid allocating names so often?
|
||||||
use std::collections::hash_map::Entry::*;
|
use std::collections::hash_map::Entry::*;
|
||||||
match self.names.entry(name.to_owned()) {
|
match self.names.entry(name.to_owned()) {
|
||||||
@@ -328,7 +394,9 @@ where
|
|||||||
Ok(id)
|
Ok(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
FuncOrDataId::Func(..) => unimplemented!(),
|
FuncOrDataId::Func(..) => Err(
|
||||||
|
ModuleError::IncompatibleDeclaration(name.to_owned()),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Vacant(entry) => {
|
Vacant(entry) => {
|
||||||
@@ -386,19 +454,24 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Define a function, producing the function body from the given `Context`.
|
/// Define a function, producing the function body from the given `Context`.
|
||||||
pub fn define_function(&mut self, func: FuncId, ctx: &mut Context) -> CtonResult {
|
pub fn define_function(&mut self, func: FuncId, ctx: &mut Context) -> Result<(), ModuleError> {
|
||||||
let compiled = {
|
let compiled = {
|
||||||
let code_size = ctx.compile(self.backend.isa())?;
|
let code_size = ctx.compile(self.backend.isa()).map_err(|e| {
|
||||||
|
dbg!(
|
||||||
|
"defining function {}: {}",
|
||||||
|
func,
|
||||||
|
ctx.func.display(self.backend.isa())
|
||||||
|
);
|
||||||
|
ModuleError::Compilation(e)
|
||||||
|
})?;
|
||||||
|
|
||||||
let info = &self.contents.functions[func];
|
let info = &self.contents.functions[func];
|
||||||
debug_assert!(
|
if !info.compiled.is_none() {
|
||||||
info.compiled.is_none(),
|
return Err(ModuleError::DuplicateDefinition(info.decl.name.clone()));
|
||||||
"functions can be defined only once"
|
}
|
||||||
);
|
if !info.decl.linkage.is_definable() {
|
||||||
debug_assert!(
|
return Err(ModuleError::InvalidImportDefinition(info.decl.name.clone()));
|
||||||
info.decl.linkage.is_definable(),
|
}
|
||||||
"imported functions cannot be defined"
|
|
||||||
);
|
|
||||||
Some(self.backend.define_function(
|
Some(self.backend.define_function(
|
||||||
&info.decl.name,
|
&info.decl.name,
|
||||||
ctx,
|
ctx,
|
||||||
@@ -413,17 +486,15 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Define a function, producing the data contents from the given `DataContext`.
|
/// Define a function, producing the data contents from the given `DataContext`.
|
||||||
pub fn define_data(&mut self, data: DataId, data_ctx: &DataContext) -> CtonResult {
|
pub fn define_data(&mut self, data: DataId, data_ctx: &DataContext) -> Result<(), ModuleError> {
|
||||||
let compiled = {
|
let compiled = {
|
||||||
let info = &self.contents.data_objects[data];
|
let info = &self.contents.data_objects[data];
|
||||||
debug_assert!(
|
if !info.compiled.is_none() {
|
||||||
info.compiled.is_none(),
|
return Err(ModuleError::DuplicateDefinition(info.decl.name.clone()));
|
||||||
"functions can be defined only once"
|
}
|
||||||
);
|
if !info.decl.linkage.is_definable() {
|
||||||
debug_assert!(
|
return Err(ModuleError::InvalidImportDefinition(info.decl.name.clone()));
|
||||||
info.decl.linkage.is_definable(),
|
}
|
||||||
"imported functions cannot be defined"
|
|
||||||
);
|
|
||||||
Some(self.backend.define_data(
|
Some(self.backend.define_data(
|
||||||
&info.decl.name,
|
&info.decl.name,
|
||||||
data_ctx,
|
data_ctx,
|
||||||
|
|||||||
@@ -2,10 +2,9 @@
|
|||||||
|
|
||||||
use cretonne_codegen::binemit::{Addend, CodeOffset, Reloc, RelocSink, NullTrapSink};
|
use cretonne_codegen::binemit::{Addend, CodeOffset, Reloc, RelocSink, NullTrapSink};
|
||||||
use cretonne_codegen::isa::TargetIsa;
|
use cretonne_codegen::isa::TargetIsa;
|
||||||
use cretonne_codegen::result::CtonError;
|
|
||||||
use cretonne_codegen::{self, ir, settings};
|
use cretonne_codegen::{self, ir, settings};
|
||||||
use cretonne_module::{Backend, DataContext, Linkage, ModuleNamespace, Writability,
|
use cretonne_module::{Backend, DataContext, Linkage, ModuleNamespace, Writability,
|
||||||
DataDescription, Init};
|
DataDescription, Init, ModuleError};
|
||||||
use cretonne_native;
|
use cretonne_native;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
@@ -116,7 +115,7 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
|
|||||||
ctx: &cretonne_codegen::Context,
|
ctx: &cretonne_codegen::Context,
|
||||||
_namespace: &ModuleNamespace<Self>,
|
_namespace: &ModuleNamespace<Self>,
|
||||||
code_size: u32,
|
code_size: u32,
|
||||||
) -> Result<Self::CompiledFunction, CtonError> {
|
) -> Result<Self::CompiledFunction, ModuleError> {
|
||||||
let size = code_size as usize;
|
let size = code_size as usize;
|
||||||
let ptr = self.code_memory.allocate(size).expect(
|
let ptr = self.code_memory.allocate(size).expect(
|
||||||
"TODO: handle OOM etc.",
|
"TODO: handle OOM etc.",
|
||||||
@@ -139,7 +138,7 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
|
|||||||
_name: &str,
|
_name: &str,
|
||||||
data: &DataContext,
|
data: &DataContext,
|
||||||
_namespace: &ModuleNamespace<Self>,
|
_namespace: &ModuleNamespace<Self>,
|
||||||
) -> Result<Self::CompiledData, CtonError> {
|
) -> Result<Self::CompiledData, ModuleError> {
|
||||||
let &DataDescription {
|
let &DataDescription {
|
||||||
writable,
|
writable,
|
||||||
ref init,
|
ref init,
|
||||||
|
|||||||
Reference in New Issue
Block a user