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:
Pat Hickey
2018-04-26 22:02:35 -07:00
committed by Dan Gohman
parent 948d5fdeec
commit ee9dcb8367
12 changed files with 252 additions and 74 deletions

View File

@@ -11,7 +11,7 @@ readme = "README.md"
[dependencies]
cretonne-codegen = { path = "../codegen", version = "0.6.0" }
cretonne-module = { path = "../module", version = "0.6.0" }
faerie = "0.2.0"
faerie = "0.3.0"
goblin = "0.0.14"
failure = "0.1.1"

View File

@@ -3,13 +3,25 @@
use container;
use cretonne_codegen::binemit::{Addend, CodeOffset, Reloc, RelocSink, NullTrapSink};
use cretonne_codegen::isa::TargetIsa;
use cretonne_codegen::result::CtonError;
use cretonne_codegen::{self, binemit, ir};
use cretonne_module::{Backend, DataContext, Linkage, ModuleNamespace, Init, DataDescription};
use faerie;
use cretonne_module::{Backend, DataContext, Linkage, ModuleNamespace, Init, DataDescription,
ModuleError};
use failure::Error;
use faerie;
use std::fs::File;
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`.
pub struct FaerieBuilder {
@@ -17,6 +29,7 @@ pub struct FaerieBuilder {
name: String,
format: container::Format,
faerie_target: faerie::Target,
collect_traps: FaerieTrapCollection,
}
impl FaerieBuilder {
@@ -24,21 +37,28 @@ impl FaerieBuilder {
/// can be passed to
/// [`Module::new`](cretonne_module/struct.Module.html#method.new].
///
/// Note: To support calls JIT'd functions from Rust or other compiled
/// code, it's necessary for the `call_conv` setting in `isa`'s flags
/// to match the host platform.
/// Faerie output requires that TargetIsa have PIC (Position Independent Code) enabled.
///
/// `collect_traps` setting determines whether trap information is collected in a
/// `FaerieTrapManifest` available in the `FaerieProduct`.
pub fn new(
isa: Box<TargetIsa>,
name: String,
format: container::Format,
) -> Result<Self, Error> {
debug_assert!(isa.flags().is_pic(), "faerie requires PIC");
collect_traps: FaerieTrapCollection,
) -> 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)?;
Ok(Self {
isa,
name,
format,
faerie_target,
collect_traps,
})
}
}
@@ -48,6 +68,7 @@ pub struct FaerieBackend {
isa: Box<TargetIsa>,
artifact: faerie::Artifact,
format: container::Format,
trap_manifest: Option<FaerieTrapManifest>,
}
pub struct FaerieCompiledFunction {}
@@ -75,6 +96,10 @@ impl Backend for FaerieBackend {
isa: builder.isa,
artifact: faerie::Artifact::new(builder.faerie_target, builder.name),
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,
namespace: &ModuleNamespace<Self>,
code_size: u32,
) -> Result<FaerieCompiledFunction, CtonError> {
) -> Result<FaerieCompiledFunction, ModuleError> {
let mut code: Vec<u8> = Vec::with_capacity(code_size as usize);
code.resize(code_size as usize, 0);
@@ -112,18 +137,29 @@ impl Backend for FaerieBackend {
name,
namespace,
};
// Ignore traps for now. For now, frontends should just avoid generating code
// that traps.
let mut trap_sink = NullTrapSink {};
unsafe {
ctx.emit_to_memory(
&*self.isa,
code.as_mut_ptr(),
&mut reloc_sink,
&mut trap_sink,
)
};
if let Some(ref mut trap_manifest) = self.trap_manifest {
let mut trap_sink = FaerieTrapSink::new(name, code_size);
unsafe {
ctx.emit_to_memory(
&*self.isa,
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(
@@ -137,7 +173,7 @@ impl Backend for FaerieBackend {
name: &str,
data_ctx: &DataContext,
namespace: &ModuleNamespace<Self>,
) -> Result<FaerieCompiledData, CtonError> {
) -> Result<FaerieCompiledData, ModuleError> {
let &DataDescription {
writable: _writable,
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 {
let to = &namespace.get_function_decl(&function_decls[id]).name;
self.artifact
@@ -171,7 +205,7 @@ impl Backend for FaerieBackend {
to,
at: offset as usize,
})
.map_err(|_e| CtonError::InvalidInput)?;
.map_err(|e| ModuleError::Backend(format!("{}", e)))?;
}
for &(offset, id, addend) in data_relocs {
debug_assert_eq!(
@@ -186,7 +220,7 @@ impl Backend for FaerieBackend {
to,
at: offset as usize,
})
.map_err(|_e| CtonError::InvalidInput)?;
.map_err(|e| ModuleError::Backend(format!("{}", e)))?;
}
self.artifact.define(name, bytes).expect(
@@ -230,6 +264,7 @@ impl Backend for FaerieBackend {
FaerieProduct {
artifact: self.artifact,
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.
/// It provides functions for writing out the object file to memory or a file.
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,
}

View File

@@ -21,13 +21,13 @@
extern crate cretonne_codegen;
extern crate cretonne_module;
extern crate faerie;
#[macro_use]
extern crate failure;
extern crate goblin;
mod backend;
mod container;
mod target;
pub mod traps;
pub use backend::{FaerieBuilder, FaerieBackend};
pub use backend::{FaerieBuilder, FaerieBackend, FaerieProduct, FaerieTrapCollection};
pub use container::Format;

View File

@@ -1,9 +1,9 @@
use cretonne_codegen::isa;
use cretonne_module::ModuleError;
use faerie::Target;
use failure::Error;
/// 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();
match name {
"x86" => Ok(if isa.flags().is_64bit() {
@@ -13,6 +13,8 @@ pub fn translate(isa: &isa::TargetIsa) -> Result<Target, Error> {
}),
"arm32" => Ok(Target::ARMv7),
"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
View 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);
}
}