Backed out 3 changesets (bug 1547682) for build bustages: "cannot find function init_frame in this scope". CLOSED TREE

Backed out changeset 815455c1634e (bug 1547682)
Backed out changeset 7bfcf5af011d (bug 1547682)
Backed out changeset 9fd0c4622f00 (bug 1547682)
This commit is contained in:
Brindusan Cristian 2019-05-02 13:54:51 +03:00
parent 2cd9914d63
commit 2a824be22f
47 changed files with 449 additions and 4760 deletions

View File

@ -24,7 +24,7 @@ replace-with = "vendored-sources"
[source."https://github.com/CraneStation/Cranelift"]
git = "https://github.com/CraneStation/Cranelift"
rev = "be8a83132df0a277da8fa3e6a9c5d03c4a05d57e"
rev = "538a0662bf90a1daa9921c10f34827ace134abf1"
replace-with = "vendored-sources"
[source.vendored-sources]

46
Cargo.lock generated
View File

@ -166,8 +166,8 @@ name = "baldrdash"
version = "0.1.0"
dependencies = [
"bindgen 0.49.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e)",
"cranelift-wasm 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e)",
"cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
"cranelift-wasm 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
"env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"target-lexicon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -586,19 +586,19 @@ dependencies = [
[[package]]
name = "cranelift-bforest"
version = "0.30.0"
source = "git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e#be8a83132df0a277da8fa3e6a9c5d03c4a05d57e"
source = "git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1#538a0662bf90a1daa9921c10f34827ace134abf1"
dependencies = [
"cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e)",
"cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
]
[[package]]
name = "cranelift-codegen"
version = "0.30.0"
source = "git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e#be8a83132df0a277da8fa3e6a9c5d03c4a05d57e"
source = "git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1#538a0662bf90a1daa9921c10f34827ace134abf1"
dependencies = [
"cranelift-bforest 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e)",
"cranelift-codegen-meta 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e)",
"cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e)",
"cranelift-bforest 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
"cranelift-codegen-meta 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
"cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -608,22 +608,22 @@ dependencies = [
[[package]]
name = "cranelift-codegen-meta"
version = "0.30.0"
source = "git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e#be8a83132df0a277da8fa3e6a9c5d03c4a05d57e"
source = "git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1#538a0662bf90a1daa9921c10f34827ace134abf1"
dependencies = [
"cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e)",
"cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
]
[[package]]
name = "cranelift-entity"
version = "0.30.0"
source = "git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e#be8a83132df0a277da8fa3e6a9c5d03c4a05d57e"
source = "git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1#538a0662bf90a1daa9921c10f34827ace134abf1"
[[package]]
name = "cranelift-frontend"
version = "0.30.0"
source = "git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e#be8a83132df0a277da8fa3e6a9c5d03c4a05d57e"
source = "git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1#538a0662bf90a1daa9921c10f34827ace134abf1"
dependencies = [
"cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e)",
"cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"target-lexicon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -631,12 +631,12 @@ dependencies = [
[[package]]
name = "cranelift-wasm"
version = "0.30.0"
source = "git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e#be8a83132df0a277da8fa3e6a9c5d03c4a05d57e"
source = "git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1#538a0662bf90a1daa9921c10f34827ace134abf1"
dependencies = [
"cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e)",
"cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e)",
"cranelift-frontend 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e)",
"cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
"cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
"cranelift-frontend 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)",
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3645,12 +3645,12 @@ dependencies = [
"checksum core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f3f46450d6f2397261af420b4ccce23807add2e45fa206410a03d66fb7f050ae"
"checksum cose 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "72fa26cb151d3ae4b70f63d67d0fed57ce04220feafafbae7f503bef7aae590d"
"checksum cose-c 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "49726015ab0ca765144fcca61e4a7a543a16b795a777fa53f554da2fffff9a94"
"checksum cranelift-bforest 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e)" = "<none>"
"checksum cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e)" = "<none>"
"checksum cranelift-codegen-meta 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e)" = "<none>"
"checksum cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e)" = "<none>"
"checksum cranelift-frontend 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e)" = "<none>"
"checksum cranelift-wasm 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=be8a83132df0a277da8fa3e6a9c5d03c4a05d57e)" = "<none>"
"checksum cranelift-bforest 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)" = "<none>"
"checksum cranelift-codegen 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)" = "<none>"
"checksum cranelift-codegen-meta 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)" = "<none>"
"checksum cranelift-entity 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)" = "<none>"
"checksum cranelift-frontend 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)" = "<none>"
"checksum cranelift-wasm 0.30.0 (git+https://github.com/CraneStation/Cranelift?rev=538a0662bf90a1daa9921c10f34827ace134abf1)" = "<none>"
"checksum crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5d02c0aac6bd68393ed69e00bbc2457f3e89075c6349db7189618dc4ddc1d7"
"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
"checksum crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fe8153ef04a7594ded05b427ffad46ddeaf22e63fd48d42b3e1e3bb4db07cae7"

View File

@ -64,8 +64,8 @@ packed_simd = { git = "https://github.com/hsivonen/packed_simd", branch = "rust_
[patch.crates-io.cranelift-codegen]
git = "https://github.com/CraneStation/Cranelift"
rev = "be8a83132df0a277da8fa3e6a9c5d03c4a05d57e"
rev = "538a0662bf90a1daa9921c10f34827ace134abf1"
[patch.crates-io.cranelift-wasm]
git = "https://github.com/CraneStation/Cranelift"
rev = "be8a83132df0a277da8fa3e6a9c5d03c4a05d57e"
rev = "538a0662bf90a1daa9921c10f34827ace134abf1"

View File

@ -162,6 +162,12 @@ static int testWasmFuzz(const uint8_t* buf, size_t size) {
}
}
// TODO: Cranelift is not stable for fuzzing, defer to baseline
if (enableWasmCranelift) {
enableWasmCranelift = false;
enableWasmBaseline = true;
}
if (enableWasmAwaitTier2) {
// Tier 2 needs Baseline + {Ion,Cranelift}
enableWasmBaseline = true;

View File

@ -409,8 +409,24 @@ BD_ConstantValue global_constantValue(const GlobalDesc* global) {
return v;
}
#ifdef DEBUG
static bool IsCraneliftCompatible(TypeCode type) {
switch (type) {
case TypeCode::I32:
case TypeCode::I64:
case TypeCode::F32:
case TypeCode::F64:
return true;
default:
return false;
}
}
#endif
TypeCode global_type(const GlobalDesc* global) {
return TypeCode(global->type().code());
TypeCode type = TypeCode(global->type().code());
MOZ_ASSERT(IsCraneliftCompatible(type));
return type;
}
size_t global_tlsOffset(const GlobalDesc* global) {
@ -433,6 +449,11 @@ size_t funcType_numArgs(const FuncTypeWithId* funcType) {
}
const BD_ValType* funcType_args(const FuncTypeWithId* funcType) {
#ifdef DEBUG
for (ValType valType : funcType->args()) {
MOZ_ASSERT(IsCraneliftCompatible(TypeCode(valType.code())));
}
#endif
static_assert(sizeof(BD_ValType) == sizeof(ValType), "update BD_ValType");
return (const BD_ValType*)&funcType->args()[0];
}

View File

@ -20,9 +20,6 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
// We need to allow dead code because the Rustc compiler complains about variants never being
// constructed in TypeCode, which is true because these values come from C++.
#![allow(dead_code)]
use cranelift_codegen::binemit::CodeOffset;
use cranelift_codegen::entity::EntityRef;

View File

@ -16,55 +16,59 @@
// Safe wrappers to the low-level ABI. This re-exports all types in
// baldrapi but none of the functions.
use baldrapi::CraneliftModuleEnvironment;
use cranelift_codegen::cursor::FuncCursor;
use cranelift_codegen::entity::EntityRef;
use cranelift_codegen::ir::immediates::{Ieee32, Ieee64};
use cranelift_codegen::ir::{self, InstBuilder};
use cranelift_wasm::{FuncIndex, GlobalIndex, SignatureIndex, TableIndex, WasmResult};
use cranelift_wasm::{FuncIndex, GlobalIndex, SignatureIndex, TableIndex};
use std::mem;
use std::slice;
use baldrapi;
use baldrapi::BD_ValType as ValType;
use baldrapi::CraneliftModuleEnvironment;
use baldrapi::TypeCode;
use utils::BasicError;
pub use baldrapi::BD_SymbolicAddress as SymbolicAddress;
pub use baldrapi::BD_ValType as ValType;
pub use baldrapi::CraneliftCompiledFunc as CompiledFunc;
pub use baldrapi::CraneliftFuncCompileInput as FuncCompileInput;
pub use baldrapi::CraneliftMetadataEntry as MetadataEntry;
pub use baldrapi::CraneliftStaticEnvironment as StaticEnvironment;
pub use baldrapi::FuncTypeIdDescKind;
pub use baldrapi::Trap;
pub use baldrapi::TypeCode;
/// Converts a `TypeCode` into the equivalent Cranelift type, if it's a known type, or an error
/// otherwise.
#[inline]
fn typecode_to_type(type_code: TypeCode) -> WasmResult<Option<ir::Type>> {
match type_code {
TypeCode::I32 => Ok(Some(ir::types::I32)),
TypeCode::I64 => Ok(Some(ir::types::I64)),
TypeCode::F32 => Ok(Some(ir::types::F32)),
TypeCode::F64 => Ok(Some(ir::types::F64)),
TypeCode::BlockVoid => Ok(None),
_ => Err(BasicError::new(format!("unknown type code: {:?}", type_code)).into()),
/// Convert a `TypeCode` into the equivalent Cranelift type.
///
/// We expect Cranelift's `VOID` type to go away in the future, so use `None` to represent a
/// function without a return value.
impl Into<Option<ir::Type>> for TypeCode {
fn into(self) -> Option<ir::Type> {
match self {
TypeCode::I32 => Some(ir::types::I32),
TypeCode::I64 => Some(ir::types::I64),
TypeCode::F32 => Some(ir::types::F32),
TypeCode::F64 => Some(ir::types::F64),
TypeCode::BlockVoid => None,
_ => panic!("unexpected type"),
}
}
}
/// Convert a non-void `TypeCode` into the equivalent Cranelift type.
#[inline]
fn typecode_to_nonvoid_type(type_code: TypeCode) -> WasmResult<ir::Type> {
Ok(typecode_to_type(type_code)?.expect("unexpected void type"))
impl Into<ir::Type> for TypeCode {
fn into(self) -> ir::Type {
match self.into() {
Some(t) => t,
None => panic!("unexpected void type"),
}
}
}
/// Convert a `TypeCode` into the equivalent Cranelift type.
#[inline]
fn valtype_to_type(val_type: ValType) -> WasmResult<ir::Type> {
let type_code = unsafe { baldrapi::env_unpack(val_type) };
typecode_to_nonvoid_type(type_code)
impl Into<ir::Type> for ValType {
fn into(self) -> ir::Type {
unsafe { baldrapi::env_unpack(self) }.into()
}
}
/// Convert a u32 into a `BD_SymbolicAddress`.
@ -79,9 +83,8 @@ impl From<u32> for SymbolicAddress {
pub struct GlobalDesc(*const baldrapi::GlobalDesc);
impl GlobalDesc {
pub fn value_type(self) -> WasmResult<ir::Type> {
let type_code = unsafe { baldrapi::global_type(self.0) };
typecode_to_nonvoid_type(type_code)
pub fn value_type(self) -> TypeCode {
unsafe { baldrapi::global_type(self.0) }
}
pub fn is_constant(self) -> bool {
@ -93,15 +96,16 @@ impl GlobalDesc {
}
/// Insert an instruction at `pos` that materialized the constant value.
pub fn emit_constant(self, pos: &mut FuncCursor) -> WasmResult<ir::Value> {
pub fn emit_constant(self, pos: &mut FuncCursor) -> ir::Value {
unsafe {
let v = baldrapi::global_constantValue(self.0);
// Note that the floating point constants below
match v.t {
TypeCode::I32 => Ok(pos.ins().iconst(ir::types::I32, i64::from(v.u.i32))),
TypeCode::I64 => Ok(pos.ins().iconst(ir::types::I64, v.u.i64)),
TypeCode::F32 => Ok(pos.ins().f32const(Ieee32::with_bits(v.u.i32 as u32))),
TypeCode::F64 => Ok(pos.ins().f64const(Ieee64::with_bits(v.u.i64 as u64))),
_ => Err(BasicError::new(format!("unexpected type: {}", v.t as u64)).into()),
TypeCode::I32 => pos.ins().iconst(ir::types::I32, i64::from(v.u.i32)),
TypeCode::I64 => pos.ins().iconst(ir::types::I64, v.u.i64),
TypeCode::F32 => pos.ins().f32const(Ieee32::with_bits(v.u.i32 as u32)),
TypeCode::F64 => pos.ins().f64const(Ieee64::with_bits(v.u.i64 as u64)),
_ => panic!("unexpected type"),
}
}
}
@ -126,27 +130,23 @@ impl TableDesc {
pub struct FuncTypeWithId(*const baldrapi::FuncTypeWithId);
impl FuncTypeWithId {
pub fn args<'a>(self) -> WasmResult<Vec<ir::Type>> {
let num_args = unsafe { baldrapi::funcType_numArgs(self.0) };
// The `funcType_args` callback crashes when there are no arguments. Also note that
// `slice::from_raw_parts()` requires a non-null pointer for empty slices.
// TODO: We should get all the parts of a signature in a single callback that returns a
// struct.
if num_args == 0 {
Ok(Vec::new())
} else {
let args = unsafe { slice::from_raw_parts(baldrapi::funcType_args(self.0), num_args) };
let mut ret = Vec::new();
for &arg in args {
ret.push(valtype_to_type(arg)?);
pub fn args<'a>(self) -> &'a [ValType] {
unsafe {
let num_args = baldrapi::funcType_numArgs(self.0);
// The `funcType_args` callback crashes when there are no arguments. Also note that
// `slice::from_raw_parts()` requires a non-null pointer for empty slices.
// TODO: We should get all the parts of a signature in a single callback that returns a
// struct.
if num_args == 0 {
&[]
} else {
slice::from_raw_parts(baldrapi::funcType_args(self.0), num_args)
}
Ok(ret)
}
}
pub fn ret_type(self) -> WasmResult<Option<ir::Type>> {
let type_code = unsafe { baldrapi::funcType_retType(self.0) };
typecode_to_type(type_code)
pub fn ret_type(self) -> TypeCode {
unsafe { baldrapi::funcType_retType(self.0) }
}
pub fn id_kind(self) -> FuncTypeIdDescKind {

View File

@ -109,7 +109,7 @@ impl<'a, 'b> BatchCompiler<'a, 'b> {
// Set up the signature before translating the WebAssembly byte code.
// The translator refers to it.
let index = FuncIndex::new(func.index as usize);
let wsig = init_sig(&mut self.context.func.signature, &self.environ, index)?;
let wsig = init_sig(&mut self.context.func.signature, &self.environ, index);
self.context.func.name = wasm_function_name(index);
let tenv = &mut TransEnv::new(&*self.isa, &self.environ, self.static_environ);

View File

@ -17,8 +17,6 @@
use std::error;
use std::fmt;
use cranelift_wasm::WasmError;
type DashError = Box<error::Error>;
pub type DashResult<T> = Result<T, DashError>;
@ -47,9 +45,3 @@ impl error::Error for BasicError {
&self.msg
}
}
impl Into<WasmError> for BasicError {
fn into(self) -> WasmError {
WasmError::User(self.into())
}
}

View File

@ -68,13 +68,13 @@ fn uimm64(offset: usize) -> ir::immediates::Uimm64 {
}
/// Initialize a `Signature` from a wasm signature.
fn init_sig_from_wsig(sig: &mut ir::Signature, wsig: bd::FuncTypeWithId) -> WasmResult<()> {
fn init_sig_from_wsig(sig: &mut ir::Signature, wsig: bd::FuncTypeWithId) {
sig.clear(CallConv::Baldrdash);
for arg in wsig.args()? {
sig.params.push(ir::AbiParam::new(arg));
for &arg in wsig.args() {
sig.params.push(ir::AbiParam::new(arg.into()));
}
if let Some(ret_type) = wsig.ret_type()? {
if let Some(ret_type) = wsig.ret_type().into() {
let ret = match ret_type {
// Spidermonkey requires i32 returns to have their high 32 bits
// zero so that it can directly box them.
@ -90,8 +90,6 @@ fn init_sig_from_wsig(sig: &mut ir::Signature, wsig: bd::FuncTypeWithId) -> Wasm
native_pointer_type(),
ir::ArgumentPurpose::VMContext,
));
Ok(())
}
/// Initialize the signature `sig` to match the function with `index` in `env`.
@ -99,10 +97,10 @@ pub fn init_sig(
sig: &mut ir::Signature,
env: &bd::ModuleEnvironment,
func_index: FuncIndex,
) -> WasmResult<bd::FuncTypeWithId> {
) -> bd::FuncTypeWithId {
let wsig = env.function_signature(func_index);
init_sig_from_wsig(sig, wsig)?;
Ok(wsig)
init_sig_from_wsig(sig, wsig);
wsig
}
/// A `TargetIsa` and `ModuleEnvironment` joined so we can implement `FuncEnvironment`.
@ -390,11 +388,7 @@ impl<'a, 'b, 'c> FuncEnvironment for TransEnv<'a, 'b, 'c> {
native_pointer_type()
}
fn make_global(
&mut self,
func: &mut ir::Function,
index: GlobalIndex,
) -> WasmResult<GlobalVariable> {
fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable {
let global = self.env.global(index);
if global.is_constant() {
// Constant globals have a known value at compile time. We insert an instruction to
@ -402,7 +396,7 @@ impl<'a, 'b, 'c> FuncEnvironment for TransEnv<'a, 'b, 'c> {
let mut pos = FuncCursor::new(func);
pos.next_ebb().expect("empty function");
pos.next_inst();
return Ok(GlobalVariable::Const(global.emit_constant(&mut pos)?));
return GlobalVariable::Const(global.emit_constant(&mut pos));
}
// This is a global variable. Here we don't care if it is mutable or not.
@ -423,16 +417,14 @@ impl<'a, 'b, 'c> FuncEnvironment for TransEnv<'a, 'b, 'c> {
(vmctx_gv, offset32(offset))
};
let mem_ty = global.value_type()?;
Ok(GlobalVariable::Memory {
GlobalVariable::Memory {
gv: base_gv,
ty: mem_ty,
ty: global.value_type().into(),
offset,
})
}
}
fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> WasmResult<ir::Heap> {
fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap {
assert_eq!(index.index(), 0, "Only one WebAssembly memory supported");
// Get the address of the `TlsData::memoryBase` field.
let base_addr = self.get_vmctx_gv(func);
@ -463,23 +455,19 @@ impl<'a, 'b, 'c> FuncEnvironment for TransEnv<'a, 'b, 'c> {
ir::HeapStyle::Dynamic { bound_gv }
};
Ok(func.create_heap(ir::HeapData {
func.create_heap(ir::HeapData {
base,
min_size,
offset_guard_size: guard_size,
style,
index_type: ir::types::I32,
}))
})
}
fn make_indirect_sig(
&mut self,
func: &mut ir::Function,
index: SignatureIndex,
) -> WasmResult<ir::SigRef> {
fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef {
let mut sigdata = ir::Signature::new(CallConv::Baldrdash);
let wsig = self.env.signature(index);
init_sig_from_wsig(&mut sigdata, wsig)?;
init_sig_from_wsig(&mut sigdata, wsig);
if wsig.id_kind() != bd::FuncTypeIdDescKind::None {
// A signature to be used for an indirect call also takes a signature id.
@ -489,10 +477,10 @@ impl<'a, 'b, 'c> FuncEnvironment for TransEnv<'a, 'b, 'c> {
));
}
Ok(func.import_signature(sigdata))
func.import_signature(sigdata)
}
fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> WasmResult<ir::Table> {
fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> ir::Table {
let table_desc = self.get_table(func, index);
// TODO we'd need a better way to synchronize the shape of GlobalDataDesc and these
@ -511,30 +499,26 @@ impl<'a, 'b, 'c> FuncEnvironment for TransEnv<'a, 'b, 'c> {
readonly: false,
});
Ok(func.create_table(ir::TableData {
func.create_table(ir::TableData {
base_gv,
min_size: ir::immediates::Uimm64::new(0),
bound_gv,
element_size: ir::immediates::Uimm64::new(u64::from(self.pointer_bytes()) * 2),
index_type: ir::types::I32,
}))
})
}
fn make_direct_func(
&mut self,
func: &mut ir::Function,
index: FuncIndex,
) -> WasmResult<ir::FuncRef> {
fn make_direct_func(&mut self, func: &mut ir::Function, index: FuncIndex) -> ir::FuncRef {
// Create a signature.
let mut sigdata = ir::Signature::new(CallConv::Baldrdash);
init_sig(&mut sigdata, &self.env, index)?;
init_sig(&mut sigdata, &self.env, index);
let signature = func.import_signature(sigdata);
Ok(func.import_function(ir::ExtFuncData {
func.import_function(ir::ExtFuncData {
name: wasm_function_name(index),
signature,
colocated: true,
}))
})
}
fn translate_call_indirect(
@ -768,10 +752,9 @@ impl<'a, 'b, 'c> FuncEnvironment for TransEnv<'a, 'b, 'c> {
Ok(pos.func.dfg.first_result(call))
}
fn translate_loop_header(&mut self, mut pos: FuncCursor) -> WasmResult<()> {
fn translate_loop_header(&mut self, mut pos: FuncCursor) {
let interrupt = self.load_interrupt_flag(&mut pos);
pos.ins().trapnz(interrupt, ir::TrapCode::Interrupt);
Ok(())
}
fn return_mode(&self) -> ReturnMode {

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"253c80832ab598570d604ae8a8108ea9835b8eef8d9b9645408ed025ce3b574a","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"b123f056d0d458396679c5f7f2a16d2762af0258fcda4ac14b6655a95e5a0022","src/cdsl/ast.rs":"3406fec29fe80c979dc938a0fddda354ed96611276068b951e2b5b23ad018338","src/cdsl/cpu_modes.rs":"57c40621115a58faa7af1c729ecaf8cda01c2b143008bde6d8c70885e7c3fd75","src/cdsl/formats.rs":"858f0a6ea62580a2812a8f2ff68dd25298b6ea4c2413657c41630a044492ea0d","src/cdsl/inst.rs":"ed31c12876ab384d0ddd2f6395a64bf3a2f847a4864147b4bb52308cdeef4980","src/cdsl/isa.rs":"201e57e580defead2d1eac82762d629de3882a6bc1c53a9726025a1eef7fd752","src/cdsl/mod.rs":"e0501574efc5d17003496ed3e1cf3cc0a15d80b687dce374f12bc689d6f525b6","src/cdsl/operands.rs":"5217258f2c4911b5be29ca020e9f7460b85182a74d485e027a3bd8336fbf2891","src/cdsl/regs.rs":"b99f24c3ecb46691625dc177b4e18d53e02265bc85a2f827a8d18381fe8f39bb","src/cdsl/settings.rs":"4ddeadf1542cc2ddec0f9e6c22d1637050da519586cd9fec0243c3eab9619f82","src/cdsl/type_inference.rs":"2771631701c150e077c5dcf705c8ae8705944d86ab945ae9e7adc82f3ca5447a","src/cdsl/types.rs":"4cc1f20eb8383fdee6a9e7ca0f7758e563a4fb715056b5edbd4db72f8dfd471b","src/cdsl/typevar.rs":"bd63d403e1ab12130c484100e5bc54b32120f27163a38cee3f546f56b9819325","src/cdsl/xform.rs":"87aeb183ef4282e05c442046df4028d771be63acc3e0250def17cdca7a953477","src/constant_hash.rs":"b8acd3f8712a4999819d9d9beced2938d9940a5748ba016c182f1132d97eefab","src/error.rs":"5110a4e3c1e97396ba02d9f5abbb8af4b586f0cc4d33a5c2473f1718cc4bef05","src/gen_inst.rs":"971104519426da5eebeae2118439e2f7fe011ac0ee54f470f6311a12ae1045b9","src/gen_legalizer.rs":"268e9922f2742c611cace9763d3904f9c113f6869ee9588ed3d19b53f6d57d56","src/gen_registers.rs":"a544a2b91fafe08639e39e50bea0892fda89fe2f6eaf111b2d5f3e98e4d07b86","src/gen_settings.rs":"77ee330b85a255c49247222f4d071da839b0520eddd3dc561867f7ae84e199ac","src/gen_types.rs":"3935da6c6a53f9332e06f74bc3a46270656b4d4231ad28ed2648d7b1d2774e90","src/isa/arm32/mod.rs":"39c168a2fc979ee1ccaddf303d590f6b50019f1a0733a426c81e9bc5d57ee90c","src/isa/arm64/mod.rs":"335e238ff1a61026c88c11b55585030960c3938cccf901ffb7d1ce3c0fa6db41","src/isa/mod.rs":"fce60d19dd3c099ebee3ac5ae64a2bee363f13da9ff5a4960d3c1a0bee71d29a","src/isa/riscv/mod.rs":"21bb24fd01d529e981948eacbeea09f7a780df985280aca4a6ed46381e04e374","src/isa/x86/instructions.rs":"bd6b02ccc79984ed4a5615ae3b20a60a4da3777495b72f771762a886f87d2335","src/isa/x86/legalize.rs":"67316297edffbb2bdf0b19ff353a634f1f609a60bd2858410c9140ad0d7ca2bf","src/isa/x86/mod.rs":"ca6fbf1e9f97ee5fe8c180521a0d5cd7758c0aa1a6c9bfb741cdaf71147a1919","src/lib.rs":"614ca19320a968db23ddcae1eb401763b9fd2e7812fe70e8b227c890f87dc7d9","src/shared/entities.rs":"e7a44d5f621d726479c3812384e78dd25e8c063d074c64d0908b3667e7d28af1","src/shared/formats.rs":"20908b1048c5e71a185de6b6ded79cdff2c26ddb38ba7b134b7a27f37e8324f3","src/shared/immediates.rs":"ac3653ce7f83372833136d28e1809d23b1dc65e7de29ffa26b5e381fcb94d25b","src/shared/instructions.rs":"2a0993279b3529b2c31aa8e83589636104a005351463ec2d3b81b5ffe569d276","src/shared/legalize.rs":"6fc4bebca916bf68b3289f4750ed11148365181b04d497d1d1c1913db7a6fe19","src/shared/mod.rs":"7029cb0c5f7ad59cb2a7ea5f8b91b0fbb363d0704df208dcf8c069da6bfa4c13","src/shared/settings.rs":"bad2dc0e1d71ee6fec6418aa79234296aa918e499a1671c3e5c1d4b0d84b6f49","src/shared/types.rs":"158d73840185e6aa8385463bbf6568efdda0c8de8284cf6b4e565f425ec5d921","src/srcgen.rs":"79fee2f603b33f76f7c9c8b9452c745a363d732c40c0814d84001ff3ef805677","src/unique_table.rs":"90b7203b29241a1ede70f0a3e50d96799e0b41d8f7455170d6ffb127f87f3cc3"},"package":null}
{"files":{"Cargo.toml":"253c80832ab598570d604ae8a8108ea9835b8eef8d9b9645408ed025ce3b574a","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"b123f056d0d458396679c5f7f2a16d2762af0258fcda4ac14b6655a95e5a0022","src/cdsl/formats.rs":"98ab61698ad4c1fb81541b1820bd1c1561810bdcff2796dec541c98b4b1901d7","src/cdsl/inst.rs":"d5130c1a36a4e33d1374f9867119c3f2d79c384f12afc12e7b7b4518cf1f74b3","src/cdsl/isa.rs":"dd52d35fa963494b7da892a4a04a4f9978079bb2d86c6af4273a8dfdb82bdf51","src/cdsl/mod.rs":"2d2e216f8c3a81978a5113213559a5ab659bc112b6194cbe08a752313aad7f46","src/cdsl/operands.rs":"cc579fd543e36cf8e82938db331c145b77e29855ee2aa8c5dd949678f980796d","src/cdsl/regs.rs":"b99f24c3ecb46691625dc177b4e18d53e02265bc85a2f827a8d18381fe8f39bb","src/cdsl/settings.rs":"4ddeadf1542cc2ddec0f9e6c22d1637050da519586cd9fec0243c3eab9619f82","src/cdsl/type_inference.rs":"8aedb2e99dee299abbc327ce3a604d48f161580776225d2438a54bbec5b725fe","src/cdsl/types.rs":"4cc1f20eb8383fdee6a9e7ca0f7758e563a4fb715056b5edbd4db72f8dfd471b","src/cdsl/typevar.rs":"605786e2bf367879da500327fc003a4d2a663259c2dee76c87e5b99b6f6331ee","src/constant_hash.rs":"b8acd3f8712a4999819d9d9beced2938d9940a5748ba016c182f1132d97eefab","src/error.rs":"5110a4e3c1e97396ba02d9f5abbb8af4b586f0cc4d33a5c2473f1718cc4bef05","src/gen_inst.rs":"795d30588a5e87e69f3510606c9aee1b8670d5aee5f3d54067f6c7508a67e565","src/gen_registers.rs":"a544a2b91fafe08639e39e50bea0892fda89fe2f6eaf111b2d5f3e98e4d07b86","src/gen_settings.rs":"77ee330b85a255c49247222f4d071da839b0520eddd3dc561867f7ae84e199ac","src/gen_types.rs":"3935da6c6a53f9332e06f74bc3a46270656b4d4231ad28ed2648d7b1d2774e90","src/isa/arm32/mod.rs":"6ed3be790b28d3115421be282a06b8c376295e1776c4b77243443799015ab70d","src/isa/arm64/mod.rs":"5c46082f68c958e83ffc636de893e2ff49fd8ce21ef357f359837ca48a60eaa5","src/isa/mod.rs":"fce60d19dd3c099ebee3ac5ae64a2bee363f13da9ff5a4960d3c1a0bee71d29a","src/isa/riscv/mod.rs":"785f0da2b04458793cb2d493c5e1eeb7ea339bc721df76dda69db3b49bcdfd27","src/isa/x86/instructions.rs":"bd6b02ccc79984ed4a5615ae3b20a60a4da3777495b72f771762a886f87d2335","src/isa/x86/mod.rs":"ba7c11aedb190f58432226a6dec8a125b385cc18fd2f70c46703d077904a3112","src/lib.rs":"99aec646c7b756c01544a181e9b56ba14fccfb2bce205a8c1f63fb31905630ca","src/shared/entities.rs":"e7a44d5f621d726479c3812384e78dd25e8c063d074c64d0908b3667e7d28af1","src/shared/formats.rs":"20908b1048c5e71a185de6b6ded79cdff2c26ddb38ba7b134b7a27f37e8324f3","src/shared/immediates.rs":"1e64836f82045d05da7c151e60cf1e66666af3e0c19179de3f37e72dc81e1bbd","src/shared/instructions.rs":"2a0993279b3529b2c31aa8e83589636104a005351463ec2d3b81b5ffe569d276","src/shared/mod.rs":"696c166d3c19bd84604583a7b8d7ec4f6671622ed581bfce8bee375d02067cbe","src/shared/settings.rs":"bad2dc0e1d71ee6fec6418aa79234296aa918e499a1671c3e5c1d4b0d84b6f49","src/shared/types.rs":"158d73840185e6aa8385463bbf6568efdda0c8de8284cf6b4e565f425ec5d921","src/srcgen.rs":"ad39143ae50f3b19f18a43131bdd3308852c70a9e532cc99f97624e7380b00d8","src/unique_table.rs":"bec9d48ee040216a7c9deab6d2c5050d7ce70e38482cc8957105fd7cbca3c33a"},"package":null}

View File

@ -1,653 +0,0 @@
use crate::cdsl::formats::FormatRegistry;
use crate::cdsl::inst::{BoundInstruction, Instruction, InstructionPredicate};
use crate::cdsl::operands::{OperandKind, OperandKindFields};
use crate::cdsl::types::{LaneType, ValueType};
use crate::cdsl::typevar::{TypeSetBuilder, TypeVar};
use cranelift_entity::{entity_impl, PrimaryMap};
use std::fmt;
pub enum Expr {
Var(VarIndex),
Literal(Literal),
Apply(Apply),
}
impl Expr {
pub fn maybe_literal(&self) -> Option<&Literal> {
match &self {
Expr::Literal(lit) => Some(lit),
_ => None,
}
}
pub fn maybe_var(&self) -> Option<VarIndex> {
if let Expr::Var(var) = &self {
Some(*var)
} else {
None
}
}
pub fn unwrap_var(&self) -> VarIndex {
self.maybe_var()
.expect("tried to unwrap a non-Var content in Expr::unwrap_var")
}
pub fn to_rust_code(&self, var_pool: &VarPool) -> String {
match self {
Expr::Var(var_index) => var_pool.get(*var_index).to_rust_code(),
Expr::Literal(literal) => literal.to_rust_code(),
Expr::Apply(a) => a.to_rust_code(var_pool),
}
}
}
/// An AST definition associates a set of variables with the values produced by an expression.
pub struct Def {
pub apply: Apply,
pub defined_vars: Vec<VarIndex>,
}
impl Def {
pub fn to_comment_string(&self, var_pool: &VarPool) -> String {
let results = self
.defined_vars
.iter()
.map(|&x| var_pool.get(x).name)
.collect::<Vec<_>>();
let results = if results.len() == 1 {
results[0].to_string()
} else {
format!("({})", results.join(", "))
};
format!("{} << {}", results, self.apply.to_comment_string(var_pool))
}
}
pub struct DefPool {
pool: PrimaryMap<DefIndex, Def>,
}
impl DefPool {
pub fn new() -> Self {
Self {
pool: PrimaryMap::new(),
}
}
pub fn get(&self, index: DefIndex) -> &Def {
self.pool.get(index).unwrap()
}
pub fn get_mut(&mut self, index: DefIndex) -> &mut Def {
self.pool.get_mut(index).unwrap()
}
pub fn next_index(&self) -> DefIndex {
self.pool.next_key()
}
pub fn create(&mut self, apply: Apply, defined_vars: Vec<VarIndex>) -> DefIndex {
self.pool.push(Def {
apply,
defined_vars,
})
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct DefIndex(u32);
entity_impl!(DefIndex);
#[derive(Debug, Clone)]
enum LiteralValue {
/// A value of an enumerated immediate operand.
///
/// Some immediate operand kinds like `intcc` and `floatcc` have an enumerated range of values
/// corresponding to a Rust enum type. An `Enumerator` object is an AST leaf node representing one
/// of the values.
Enumerator(&'static str),
/// A bitwise value of an immediate operand, used for bitwise exact floating point constants.
Bits(u64),
/// A value of an integer immediate operand.
Int(i64),
}
#[derive(Clone)]
pub struct Literal {
kind: OperandKind,
value: LiteralValue,
}
impl fmt::Debug for Literal {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"Literal(kind={}, value={:?})",
self.kind.name, self.value
)
}
}
impl Literal {
pub fn enumerator_for(kind: &OperandKind, value: &'static str) -> Self {
if let OperandKindFields::ImmEnum(values) = &kind.fields {
assert!(
values.get(value).is_some(),
format!(
"nonexistent value '{}' in enumeration '{}'",
value, kind.name
)
);
} else {
panic!("enumerator is for enum values");
}
Self {
kind: kind.clone(),
value: LiteralValue::Enumerator(value),
}
}
pub fn bits(kind: &OperandKind, bits: u64) -> Self {
match kind.fields {
OperandKindFields::ImmValue => {}
_ => panic!("bits_of is for immediate scalar types"),
}
Self {
kind: kind.clone(),
value: LiteralValue::Bits(bits),
}
}
pub fn constant(kind: &OperandKind, value: i64) -> Self {
match kind.fields {
OperandKindFields::ImmValue => {}
_ => panic!("bits_of is for immediate scalar types"),
}
Self {
kind: kind.clone(),
value: LiteralValue::Int(value),
}
}
pub fn to_rust_code(&self) -> String {
let maybe_values = match &self.kind.fields {
OperandKindFields::ImmEnum(values) => Some(values),
OperandKindFields::ImmValue => None,
_ => panic!("impossible per construction"),
};
match self.value {
LiteralValue::Enumerator(value) => {
format!("{}::{}", self.kind.rust_type, maybe_values.unwrap()[value])
}
LiteralValue::Bits(bits) => format!("{}::with_bits({:#x})", self.kind.rust_type, bits),
LiteralValue::Int(val) => val.to_string(),
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum PatternPosition {
Source,
Destination,
}
/// A free variable.
///
/// When variables are used in `XForms` with source and destination patterns, they are classified
/// as follows:
///
/// Input values: Uses in the source pattern with no preceding def. These may appear as inputs in
/// the destination pattern too, but no new inputs can be introduced.
///
/// Output values: Variables that are defined in both the source and destination pattern. These
/// values may have uses outside the source pattern, and the destination pattern must compute the
/// same value.
///
/// Intermediate values: Values that are defined in the source pattern, but not in the destination
/// pattern. These may have uses outside the source pattern, so the defining instruction can't be
/// deleted immediately.
///
/// Temporary values are defined only in the destination pattern.
pub struct Var {
pub name: &'static str,
/// The `Def` defining this variable in a source pattern.
pub src_def: Option<DefIndex>,
/// The `Def` defining this variable in a destination pattern.
pub dst_def: Option<DefIndex>,
/// TypeVar representing the type of this variable.
type_var: Option<TypeVar>,
/// Is this the original type variable, or has it be redefined with set_typevar?
is_original_type_var: bool,
}
impl Var {
fn new(name: &'static str) -> Self {
Self {
name,
src_def: None,
dst_def: None,
type_var: None,
is_original_type_var: false,
}
}
/// Is this an input value to the src pattern?
pub fn is_input(&self) -> bool {
self.src_def.is_none() && self.dst_def.is_none()
}
/// Is this an output value, defined in both src and dst patterns?
pub fn is_output(&self) -> bool {
self.src_def.is_some() && self.dst_def.is_some()
}
/// Is this an intermediate value, defined only in the src pattern?
pub fn is_intermediate(&self) -> bool {
self.src_def.is_some() && self.dst_def.is_none()
}
/// Is this a temp value, defined only in the dst pattern?
pub fn is_temp(&self) -> bool {
self.src_def.is_none() && self.dst_def.is_some()
}
/// Get the def of this variable according to the position.
pub fn get_def(&self, position: PatternPosition) -> Option<DefIndex> {
match position {
PatternPosition::Source => self.src_def,
PatternPosition::Destination => self.dst_def,
}
}
pub fn set_def(&mut self, position: PatternPosition, def: DefIndex) {
assert!(
self.get_def(position).is_none(),
format!("redefinition of variable {}", self.name)
);
match position {
PatternPosition::Source => {
self.src_def = Some(def);
}
PatternPosition::Destination => {
self.dst_def = Some(def);
}
}
}
/// Get the type variable representing the type of this variable.
pub fn get_or_create_typevar(&mut self) -> TypeVar {
match &self.type_var {
Some(tv) => tv.clone(),
None => {
// Create a new type var in which we allow all types.
let tv = TypeVar::new(
format!("typeof_{}", self.name),
format!("Type of the pattern variable {:?}", self),
TypeSetBuilder::all(),
);
self.type_var = Some(tv.clone());
self.is_original_type_var = true;
tv
}
}
}
pub fn get_typevar(&self) -> Option<TypeVar> {
self.type_var.clone()
}
pub fn set_typevar(&mut self, tv: TypeVar) {
self.is_original_type_var = if let Some(previous_tv) = &self.type_var {
*previous_tv == tv
} else {
false
};
self.type_var = Some(tv);
}
/// Check if this variable has a free type variable. If not, the type of this variable is
/// computed from the type of another variable.
pub fn has_free_typevar(&self) -> bool {
match &self.type_var {
Some(tv) => tv.base.is_none() && self.is_original_type_var,
None => false,
}
}
pub fn to_rust_code(&self) -> String {
self.name.into()
}
fn rust_type(&self) -> String {
self.type_var.as_ref().unwrap().to_rust_code()
}
}
impl fmt::Debug for Var {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt.write_fmt(format_args!(
"Var({}{}{})",
self.name,
if self.src_def.is_some() { ", src" } else { "" },
if self.dst_def.is_some() { ", dst" } else { "" }
))
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct VarIndex(u32);
entity_impl!(VarIndex);
pub struct VarPool {
pool: PrimaryMap<VarIndex, Var>,
}
impl VarPool {
pub fn new() -> Self {
Self {
pool: PrimaryMap::new(),
}
}
pub fn get(&self, index: VarIndex) -> &Var {
self.pool.get(index).unwrap()
}
pub fn get_mut(&mut self, index: VarIndex) -> &mut Var {
self.pool.get_mut(index).unwrap()
}
pub fn create(&mut self, name: &'static str) -> VarIndex {
self.pool.push(Var::new(name))
}
}
pub enum ApplyTarget {
Inst(Instruction),
Bound(BoundInstruction),
}
impl ApplyTarget {
pub fn inst(&self) -> &Instruction {
match &self {
ApplyTarget::Inst(inst) => inst,
ApplyTarget::Bound(bound_inst) => &bound_inst.inst,
}
}
}
impl Into<ApplyTarget> for &Instruction {
fn into(self) -> ApplyTarget {
ApplyTarget::Inst(self.clone())
}
}
impl Into<ApplyTarget> for BoundInstruction {
fn into(self) -> ApplyTarget {
ApplyTarget::Bound(self)
}
}
pub fn bind(target: impl Into<ApplyTarget>, lane_type: impl Into<LaneType>) -> BoundInstruction {
let value_type = ValueType::from(lane_type.into());
let (inst, value_types) = match target.into() {
ApplyTarget::Inst(inst) => (inst, vec![value_type]),
ApplyTarget::Bound(bound_inst) => {
let mut new_value_types = bound_inst.value_types;
new_value_types.push(value_type);
(bound_inst.inst, new_value_types)
}
};
match &inst.polymorphic_info {
Some(poly) => {
assert!(
value_types.len() <= 1 + poly.other_typevars.len(),
format!("trying to bind too many types for {}", inst.name)
);
}
None => {
panic!(format!(
"trying to bind a type for {} which is not a polymorphic instruction",
inst.name
));
}
}
BoundInstruction { inst, value_types }
}
/// Apply an instruction to arguments.
///
/// An `Apply` AST expression is created by using function call syntax on instructions. This
/// applies to both bound and unbound polymorphic instructions.
pub struct Apply {
pub inst: Instruction,
pub args: Vec<Expr>,
pub value_types: Vec<ValueType>,
}
impl Apply {
pub fn new(target: ApplyTarget, args: Vec<Expr>) -> Self {
let (inst, value_types) = match target.into() {
ApplyTarget::Inst(inst) => (inst, Vec::new()),
ApplyTarget::Bound(bound_inst) => (bound_inst.inst, bound_inst.value_types),
};
// Basic check on number of arguments.
assert!(
inst.operands_in.len() == args.len(),
format!("incorrect number of arguments in instruction {}", inst.name)
);
// Check that the kinds of Literals arguments match the expected operand.
for &imm_index in &inst.imm_opnums {
let arg = &args[imm_index];
if let Some(literal) = arg.maybe_literal() {
let op = &inst.operands_in[imm_index];
assert!(
op.kind.name == literal.kind.name,
format!(
"Passing literal of kind {} to field of wrong kind {}",
literal.kind.name, op.kind.name
)
);
}
}
Self {
inst,
args,
value_types,
}
}
fn to_comment_string(&self, var_pool: &VarPool) -> String {
let args = self
.args
.iter()
.map(|arg| arg.to_rust_code(var_pool))
.collect::<Vec<_>>()
.join(", ");
let mut inst_and_bound_types = vec![self.inst.name.to_string()];
inst_and_bound_types.extend(self.value_types.iter().map(|vt| vt.to_string()));
let inst_name = inst_and_bound_types.join(".");
format!("{}({})", inst_name, args)
}
fn to_rust_code(&self, var_pool: &VarPool) -> String {
let args = self
.args
.iter()
.map(|arg| arg.to_rust_code(var_pool))
.collect::<Vec<_>>()
.join(", ");
format!("{}({})", self.inst.name, args)
}
fn inst_predicate(
&self,
format_registry: &FormatRegistry,
var_pool: &VarPool,
) -> InstructionPredicate {
let iform = format_registry.get(self.inst.format);
let mut pred = InstructionPredicate::new();
for (format_field, &op_num) in iform.imm_fields.iter().zip(self.inst.imm_opnums.iter()) {
let arg = &self.args[op_num];
if arg.maybe_var().is_some() {
// Ignore free variables for now.
continue;
}
pred = pred.and(InstructionPredicate::new_is_field_equal(
&format_field,
arg.to_rust_code(var_pool),
));
}
// Add checks for any bound secondary type variables. We can't check the controlling type
// variable this way since it may not appear as the type of an operand.
if self.value_types.len() > 1 {
let poly = self
.inst
.polymorphic_info
.as_ref()
.expect("must have polymorphic info if it has bounded types");
for (bound_type, type_var) in
self.value_types[1..].iter().zip(poly.other_typevars.iter())
{
pred = pred.and(InstructionPredicate::new_typevar_check(
&self.inst, type_var, bound_type,
));
}
}
pred
}
/// Same as `inst_predicate()`, but also check the controlling type variable.
pub fn inst_predicate_with_ctrl_typevar(
&self,
format_registry: &FormatRegistry,
var_pool: &VarPool,
) -> InstructionPredicate {
let mut pred = self.inst_predicate(format_registry, var_pool);
if !self.value_types.is_empty() {
let bound_type = &self.value_types[0];
let poly = self.inst.polymorphic_info.as_ref().unwrap();
let type_check = if poly.use_typevar_operand {
InstructionPredicate::new_typevar_check(&self.inst, &poly.ctrl_typevar, bound_type)
} else {
InstructionPredicate::new_ctrl_typevar_check(&bound_type)
};
pred = pred.and(type_check);
}
pred
}
pub fn rust_builder(&self, defined_vars: &Vec<VarIndex>, var_pool: &VarPool) -> String {
let mut args = self
.args
.iter()
.map(|expr| expr.to_rust_code(var_pool))
.collect::<Vec<_>>()
.join(", ");
// Do we need to pass an explicit type argument?
if let Some(poly) = &self.inst.polymorphic_info {
if !poly.use_typevar_operand {
args = format!("{}, {}", var_pool.get(defined_vars[0]).rust_type(), args);
}
}
format!("{}({})", self.inst.snake_name(), args)
}
}
// Simple helpers for legalize actions construction.
pub enum DummyExpr {
Var(DummyVar),
Literal(Literal),
Apply(ApplyTarget, Vec<DummyExpr>),
}
#[derive(Clone)]
pub struct DummyVar {
pub name: &'static str,
}
impl Into<DummyExpr> for DummyVar {
fn into(self) -> DummyExpr {
DummyExpr::Var(self)
}
}
impl Into<DummyExpr> for Literal {
fn into(self) -> DummyExpr {
DummyExpr::Literal(self)
}
}
pub fn var(name: &'static str) -> DummyVar {
DummyVar { name }
}
pub struct DummyDef {
pub expr: DummyExpr,
pub defined_vars: Vec<DummyVar>,
}
pub struct ExprBuilder {
expr: DummyExpr,
}
impl ExprBuilder {
pub fn apply(inst: ApplyTarget, args: Vec<DummyExpr>) -> Self {
let expr = DummyExpr::Apply(inst, args);
Self { expr }
}
pub fn assign_to(self, defined_vars: Vec<DummyVar>) -> DummyDef {
DummyDef {
expr: self.expr,
defined_vars,
}
}
}
macro_rules! def_rhs {
// inst(a, b, c)
($inst:ident($($src:expr),*)) => {
ExprBuilder::apply($inst.into(), vec![$($src.clone().into()),*])
};
// inst.type(a, b, c)
($inst:ident.$type:ident($($src:expr),*)) => {
ExprBuilder::apply(bind($inst, $type).into(), vec![$($src.clone().into()),*])
};
}
// Helper macro to define legalization recipes.
macro_rules! def {
// x = ...
($dest:ident = $($tt:tt)*) => {
def_rhs!($($tt)*).assign_to(vec![$dest.clone()])
};
// (x, y, ...) = ...
(($($dest:ident),*) = $($tt:tt)*) => {
def_rhs!($($tt)*).assign_to(vec![$($dest.clone()),*])
};
// An instruction with no results.
($($tt:tt)*) => {
def_rhs!($($tt)*).assign_to(Vec::new())
}
}

View File

@ -1,84 +0,0 @@
use crate::cdsl::types::LaneType;
use crate::cdsl::xform::{TransformGroup, TransformGroupIndex, TransformGroups};
use std::collections::{HashMap, HashSet};
use std::iter::FromIterator;
pub struct CpuMode {
_name: &'static str,
default_legalize: Option<TransformGroupIndex>,
monomorphic_legalize: Option<TransformGroupIndex>,
typed_legalize: HashMap<String, TransformGroupIndex>,
}
impl CpuMode {
pub fn new(name: &'static str) -> Self {
Self {
_name: name,
default_legalize: None,
monomorphic_legalize: None,
typed_legalize: HashMap::new(),
}
}
pub fn legalize_monomorphic(&mut self, group: &TransformGroup) {
assert!(self.monomorphic_legalize.is_none());
self.monomorphic_legalize = Some(group.id);
}
pub fn legalize_default(&mut self, group: &TransformGroup) {
assert!(self.default_legalize.is_none());
self.default_legalize = Some(group.id);
}
pub fn legalize_type(&mut self, lane_type: impl Into<LaneType>, group: &TransformGroup) {
assert!(self
.typed_legalize
.insert(lane_type.into().to_string(), group.id)
.is_none());
}
/// Returns a deterministically ordered, deduplicated list of TransformGroupIndex for the
/// transitive set of TransformGroup this TargetIsa uses.
pub fn transitive_transform_groups(
&self,
all_groups: &TransformGroups,
) -> Vec<TransformGroupIndex> {
let mut roots = Vec::new();
if let Some(i) = &self.default_legalize {
roots.push(*i);
}
if let Some(i) = &self.monomorphic_legalize {
roots.push(*i);
}
roots.extend(self.typed_legalize.values().cloned());
let mut set = HashSet::new();
for root in roots {
set.insert(root);
let mut base = root;
// Follow the chain of chain_with.
while let Some(chain_with) = &all_groups.get(base).chain_with {
set.insert(*chain_with);
base = *chain_with;
}
}
let mut ret = Vec::from_iter(set);
ret.sort();
ret
}
/// Returns a deterministically ordered, deduplicated list of TransformGroupIndex for the directly
/// reachable set of TransformGroup this TargetIsa uses.
pub fn direct_transform_groups(&self) -> Vec<TransformGroupIndex> {
let mut set = HashSet::new();
if let Some(i) = &self.default_legalize {
set.insert(*i);
}
if let Some(i) = &self.monomorphic_legalize {
set.insert(*i);
}
set.extend(self.typed_legalize.values().cloned());
let mut ret = Vec::from_iter(set);
ret.sort();
ret
}
}

View File

@ -10,6 +10,10 @@ use cranelift_entity::{entity_impl, PrimaryMap};
///
/// This corresponds to a single member of a variant of the `InstructionData`
/// data type.
///
/// :param iform: Parent `InstructionFormat`.
/// :param kind: Immediate Operand kind.
/// :param member: Member name in `InstructionData` variant.
#[derive(Debug)]
pub struct FormatField {
/// Immediate operand number in parent.
@ -18,36 +22,39 @@ pub struct FormatField {
/// Immediate operand kind.
pub kind: OperandKind,
/// Member name in InstructionData variant.
/// Member name in InstructionDate variant.
pub member: &'static str,
}
/// Every instruction opcode has a corresponding instruction format which determines the number of
/// operands and their kinds. Instruction formats are identified structurally, i.e., the format of
/// an instruction is derived from the kinds of operands used in its declaration.
/// Every instruction opcode has a corresponding instruction format which
/// determines the number of operands and their kinds. Instruction formats are
/// identified structurally, i.e., the format of an instruction is derived from
/// the kinds of operands used in its declaration.
///
/// The instruction format stores two separate lists of operands: Immediates and values. Immediate
/// operands (including entity references) are represented as explicit members in the
/// `InstructionData` variants. The value operands are stored differently, depending on how many
/// there are. Beyond a certain point, instruction formats switch to an external value list for
/// storing value arguments. Value lists can hold an arbitrary number of values.
/// The instruction format stores two separate lists of operands: Immediates
/// and values. Immediate operands (including entity references) are
/// represented as explicit members in the `InstructionData` variants. The
/// value operands are stored differently, depending on how many there are.
/// Beyond a certain point, instruction formats switch to an external value
/// list for storing value arguments. Value lists can hold an arbitrary number
/// of values.
///
/// All instruction formats must be predefined in the meta shared/formats.rs module.
/// All instruction formats must be predefined in the meta shared/formats module.
///
/// :param kinds: List of `OperandKind` objects describing the operands.
/// :param name: Instruction format name in CamelCase. This is used as a Rust
/// variant name in both the `InstructionData` and `InstructionFormat`
/// enums.
/// :param typevar_operand: Index of the value input operand that is used to
/// infer the controlling type variable. By default, this is `0`, the first
/// `value` operand. The index is relative to the values only, ignoring
/// immediate operands.
#[derive(Debug)]
pub struct InstructionFormat {
/// Instruction format name in CamelCase. This is used as a Rust variant name in both the
/// `InstructionData` and `InstructionFormat` enums.
pub name: &'static str,
pub num_value_operands: usize,
pub has_value_list: bool,
pub imm_fields: Vec<FormatField>,
/// Index of the value input operand that is used to infer the controlling type variable. By
/// default, this is `0`, the first `value` operand. The index is relative to the values only,
/// ignoring immediate operands.
pub typevar_operand: Option<usize>,
}
@ -155,7 +162,7 @@ impl InstructionFormatBuilder {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct InstructionFormatIndex(u32);
entity_impl!(InstructionFormatIndex);

View File

@ -1,15 +1,10 @@
use crate::cdsl::camel_case;
use crate::cdsl::formats::{
FormatField, FormatRegistry, InstructionFormat, InstructionFormatIndex,
};
use crate::cdsl::formats::{FormatRegistry, InstructionFormat, InstructionFormatIndex};
use crate::cdsl::operands::Operand;
use crate::cdsl::type_inference::Constraint;
use crate::cdsl::types::ValueType;
use crate::cdsl::typevar::TypeVar;
use std::fmt;
use std::ops;
use std::rc::Rc;
use std::slice;
/// Every instruction must belong to exactly one instruction group. A given
@ -37,13 +32,6 @@ impl InstructionGroup {
pub fn iter(&self) -> slice::Iter<Instruction> {
self.instructions.iter()
}
pub fn by_name(&self, name: &'static str) -> &Instruction {
self.instructions
.iter()
.find(|inst| inst.name == name)
.expect(&format!("unexisting instruction with name {}", name))
}
}
pub struct PolymorphicInfo {
@ -52,7 +40,7 @@ pub struct PolymorphicInfo {
pub other_typevars: Vec<TypeVar>,
}
pub struct InstructionContent {
pub struct Instruction {
/// Instruction mnemonic, also becomes opcode name.
pub name: &'static str,
pub camel_name: String,
@ -65,7 +53,7 @@ pub struct InstructionContent {
/// Output operands. The output operands must be SSA values or `variable_args`.
pub operands_out: Vec<Operand>,
/// Instruction-specific TypeConstraints.
pub constraints: Vec<Constraint>,
_constraints: Vec<Constraint>,
/// Instruction format, automatically derived from the input operands.
pub format: InstructionFormatIndex,
@ -102,18 +90,6 @@ pub struct InstructionContent {
pub writes_cpu_flags: bool,
}
#[derive(Clone)]
pub struct Instruction {
content: Rc<InstructionContent>,
}
impl ops::Deref for Instruction {
type Target = InstructionContent;
fn deref(&self) -> &Self::Target {
&*self.content
}
}
impl Instruction {
pub fn snake_name(&self) -> &'static str {
if self.name == "return" {
@ -132,17 +108,6 @@ impl Instruction {
}
""
}
pub fn all_typevars(&self) -> Vec<&TypeVar> {
match &self.polymorphic_info {
Some(poly) => {
let mut result = vec![&poly.ctrl_typevar];
result.extend(&poly.other_typevars);
result
}
None => Vec::new(),
}
}
}
impl fmt::Display for Instruction {
@ -307,40 +272,32 @@ impl InstructionBuilder {
let writes_cpu_flags = operands_out.iter().any(|op| op.is_cpu_flags());
Instruction {
content: Rc::new(InstructionContent {
name: self.name,
camel_name: camel_case(self.name),
doc: self.doc,
operands_in,
operands_out,
constraints: self.constraints.unwrap_or_else(Vec::new),
format: format_index,
polymorphic_info,
value_opnums,
value_results,
imm_opnums,
is_terminator: self.is_terminator,
is_branch: self.is_branch,
is_indirect_branch: self.is_indirect_branch,
is_call: self.is_call,
is_return: self.is_return,
is_ghost: self.is_ghost,
can_load: self.can_load,
can_store: self.can_store,
can_trap: self.can_trap,
other_side_effects: self.other_side_effects,
writes_cpu_flags,
}),
name: self.name,
camel_name: camel_case(self.name),
doc: self.doc,
operands_in,
operands_out,
_constraints: self.constraints.unwrap_or_else(Vec::new),
format: format_index,
polymorphic_info,
value_opnums,
value_results,
imm_opnums,
is_terminator: self.is_terminator,
is_branch: self.is_branch,
is_indirect_branch: self.is_indirect_branch,
is_call: self.is_call,
is_return: self.is_return,
is_ghost: self.is_ghost,
can_load: self.can_load,
can_store: self.can_store,
can_trap: self.can_trap,
other_side_effects: self.other_side_effects,
writes_cpu_flags,
}
}
}
#[derive(Clone)]
pub struct BoundInstruction {
pub inst: Instruction,
pub value_types: Vec<ValueType>,
}
/// Check if this instruction is polymorphic, and verify its use of type variables.
fn verify_polymorphic(
operands_in: &Vec<Operand>,
@ -499,102 +456,3 @@ fn verify_ctrl_typevar(
Ok(other_typevars)
}
/// A basic node in an instruction predicate: either an atom, or an AND of two conditions.
pub enum InstructionPredicateNode {
/// Is the field member (first member) equal to the actual argument (which name is the second
/// field)?
IsFieldEqual(String, String),
/// Is the value argument (at the index designated by the first member) the same type as the
/// type name (second member)?
TypeVarCheck(usize, String),
/// Is the controlling type variable the same type as the one designated by the type name
/// (only member)?
CtrlTypeVarCheck(String),
/// A combination of two other predicates.
And(Vec<InstructionPredicateNode>),
}
impl InstructionPredicateNode {
fn rust_predicate(&self) -> String {
match self {
InstructionPredicateNode::IsFieldEqual(field_name, arg) => {
let new_args = vec![field_name.clone(), arg.clone()];
format!("crate::predicates::is_equal({})", new_args.join(", "))
}
InstructionPredicateNode::TypeVarCheck(index, value_type_name) => format!(
"func.dfg.value_type(args[{}]) == {}",
index, value_type_name
),
InstructionPredicateNode::CtrlTypeVarCheck(value_type_name) => {
format!("func.dfg.ctrl_typevar(inst) == {}", value_type_name)
}
InstructionPredicateNode::And(nodes) => nodes
.iter()
.map(|x| x.rust_predicate())
.collect::<Vec<_>>()
.join(" &&\n"),
}
}
}
pub struct InstructionPredicate {
node: Option<InstructionPredicateNode>,
}
impl InstructionPredicate {
pub fn new() -> Self {
Self { node: None }
}
pub fn new_typevar_check(
inst: &Instruction,
type_var: &TypeVar,
value_type: &ValueType,
) -> InstructionPredicateNode {
let index = inst
.value_opnums
.iter()
.enumerate()
.filter(|(_, &op_num)| inst.operands_in[op_num].type_var().unwrap() == type_var)
.next()
.unwrap()
.0;
InstructionPredicateNode::TypeVarCheck(index, value_type.rust_name())
}
pub fn new_is_field_equal(
format_field: &FormatField,
imm_value: String,
) -> InstructionPredicateNode {
InstructionPredicateNode::IsFieldEqual(format_field.member.into(), imm_value)
}
pub fn new_ctrl_typevar_check(value_type: &ValueType) -> InstructionPredicateNode {
InstructionPredicateNode::CtrlTypeVarCheck(value_type.rust_name())
}
pub fn and(mut self, new_node: InstructionPredicateNode) -> Self {
let node = self.node;
let mut and_nodes = match node {
Some(node) => match node {
InstructionPredicateNode::And(nodes) => nodes,
_ => vec![node],
},
_ => Vec::new(),
};
and_nodes.push(new_node);
self.node = Some(InstructionPredicateNode::And(and_nodes));
self
}
pub fn rust_predicate(&self) -> String {
match &self.node {
Some(root) => root.rust_predicate(),
None => "true".into(),
}
}
}

View File

@ -1,18 +1,12 @@
use crate::cdsl::cpu_modes::CpuMode;
use crate::cdsl::inst::InstructionGroup;
use crate::cdsl::regs::IsaRegs;
use crate::cdsl::settings::SettingGroup;
use crate::cdsl::xform::{TransformGroupIndex, TransformGroups};
use std::collections::HashSet;
use std::iter::FromIterator;
pub struct TargetIsa {
pub name: &'static str,
pub instructions: InstructionGroup,
pub settings: SettingGroup,
pub regs: IsaRegs,
pub cpu_modes: Vec<CpuMode>,
}
impl TargetIsa {
@ -21,41 +15,12 @@ impl TargetIsa {
instructions: InstructionGroup,
settings: SettingGroup,
regs: IsaRegs,
cpu_modes: Vec<CpuMode>,
) -> Self {
Self {
name,
instructions,
settings,
regs,
cpu_modes,
}
}
/// Returns a deterministically ordered, deduplicated list of TransformGroupIndex for the
/// transitive set of TransformGroup this TargetIsa uses.
pub fn transitive_transform_groups(
&self,
all_groups: &TransformGroups,
) -> Vec<TransformGroupIndex> {
let mut set = HashSet::new();
for cpu_mode in &self.cpu_modes {
set.extend(cpu_mode.transitive_transform_groups(all_groups));
}
let mut vec = Vec::from_iter(set);
vec.sort();
vec
}
/// Returns a deterministically ordered, deduplicated list of TransformGroupIndex for the directly
/// reachable set of TransformGroup this TargetIsa uses.
pub fn direct_transform_groups(&self) -> Vec<TransformGroupIndex> {
let mut set = HashSet::new();
for cpu_mode in &self.cpu_modes {
set.extend(cpu_mode.direct_transform_groups());
}
let mut vec = Vec::from_iter(set);
vec.sort();
vec
}
}

View File

@ -3,9 +3,6 @@
//! This module defines the classes that are used to define Cranelift
//! instructions and other entities.
#[macro_use]
pub mod ast;
pub mod cpu_modes;
pub mod formats;
pub mod inst;
pub mod isa;
@ -15,7 +12,6 @@ pub mod settings;
pub mod type_inference;
pub mod types;
pub mod typevar;
pub mod xform;
/// A macro that converts boolean settings into predicates to look more natural.
#[macro_export]

View File

@ -134,7 +134,7 @@ pub struct OperandKind {
/// The camel-cased name of an operand kind is also the Rust type used to represent it.
pub rust_type: String,
pub fields: OperandKindFields,
fields: OperandKindFields,
}
impl OperandKind {

View File

@ -1,658 +1,5 @@
use crate::cdsl::ast::{Def, DefIndex, DefPool, Var, VarIndex, VarPool};
use crate::cdsl::typevar::{DerivedFunc, TypeSet, TypeVar};
use crate::cdsl::typevar::TypeVar;
use std::collections::{HashMap, HashSet};
use std::iter::FromIterator;
#[derive(Hash, PartialEq, Eq)]
pub enum Constraint {
/// Constraint specifying that a type var tv1 must be wider than or equal to type var tv2 at
/// runtime. This requires that:
/// 1) They have the same number of lanes
/// 2) In a lane tv1 has at least as many bits as tv2.
WiderOrEq(TypeVar, TypeVar),
/// Constraint specifying that two derived type vars must have the same runtime type.
Eq(TypeVar, TypeVar),
/// Constraint specifying that a type var must belong to some typeset.
InTypeset(TypeVar, TypeSet),
}
impl Constraint {
fn translate_with<F: Fn(&TypeVar) -> TypeVar>(&self, func: F) -> Constraint {
match self {
Constraint::WiderOrEq(lhs, rhs) => {
let lhs = func(&lhs);
let rhs = func(&rhs);
Constraint::WiderOrEq(lhs, rhs)
}
Constraint::Eq(lhs, rhs) => {
let lhs = func(&lhs);
let rhs = func(&rhs);
Constraint::Eq(lhs, rhs)
}
Constraint::InTypeset(tv, ts) => {
let tv = func(&tv);
Constraint::InTypeset(tv, ts.clone())
}
}
}
/// Creates a new constraint by replacing type vars by their hashmap equivalent.
fn translate_with_map(
&self,
original_to_own_typevar: &HashMap<&TypeVar, TypeVar>,
) -> Constraint {
self.translate_with(|tv| substitute(original_to_own_typevar, tv))
}
/// Creates a new constraint by replacing type vars by their canonical equivalent.
fn translate_with_env(&self, type_env: &TypeEnvironment) -> Constraint {
self.translate_with(|tv| type_env.get_equivalent(tv))
}
fn is_trivial(&self) -> bool {
match self {
Constraint::WiderOrEq(lhs, rhs) => {
// Trivially true.
if lhs == rhs {
return true;
}
let ts1 = lhs.get_typeset();
let ts2 = rhs.get_typeset();
// Trivially true.
if ts1.is_wider_or_equal(&ts2) {
return true;
}
// Trivially false.
if ts1.is_narrower(&ts2) {
return true;
}
// Trivially false.
if (&ts1.lanes & &ts2.lanes).len() == 0 {
return true;
}
self.is_concrete()
}
Constraint::Eq(lhs, rhs) => lhs == rhs || self.is_concrete(),
Constraint::InTypeset(_, _) => {
// The way InTypeset are made, they would always be trivial if we were applying the
// same logic as the Python code did, so ignore this.
self.is_concrete()
}
}
}
/// Returns true iff all the referenced type vars are singletons.
fn is_concrete(&self) -> bool {
match self {
Constraint::WiderOrEq(lhs, rhs) => {
lhs.singleton_type().is_some() && rhs.singleton_type().is_some()
}
Constraint::Eq(lhs, rhs) => {
lhs.singleton_type().is_some() && rhs.singleton_type().is_some()
}
Constraint::InTypeset(tv, _) => tv.singleton_type().is_some(),
}
}
fn typevar_args(&self) -> Vec<&TypeVar> {
match self {
Constraint::WiderOrEq(lhs, rhs) => vec![lhs, rhs],
Constraint::Eq(lhs, rhs) => vec![lhs, rhs],
Constraint::InTypeset(tv, _) => vec![tv],
}
}
}
#[derive(Clone, Copy)]
enum TypeEnvRank {
Singleton = 5,
Input = 4,
Intermediate = 3,
Output = 2,
Temp = 1,
Internal = 0,
}
/// Class encapsulating the necessary bookkeeping for type inference.
pub struct TypeEnvironment {
vars: HashSet<VarIndex>,
ranks: HashMap<TypeVar, TypeEnvRank>,
equivalency_map: HashMap<TypeVar, TypeVar>,
pub constraints: Vec<Constraint>,
}
impl TypeEnvironment {
fn new() -> Self {
TypeEnvironment {
vars: HashSet::new(),
ranks: HashMap::new(),
equivalency_map: HashMap::new(),
constraints: Vec::new(),
}
}
fn register(&mut self, var_index: VarIndex, var: &mut Var) {
self.vars.insert(var_index);
let rank = if var.is_input() {
TypeEnvRank::Input
} else if var.is_intermediate() {
TypeEnvRank::Intermediate
} else if var.is_output() {
TypeEnvRank::Output
} else {
assert!(var.is_temp());
TypeEnvRank::Temp
};
self.ranks.insert(var.get_or_create_typevar(), rank);
}
fn add_constraint(&mut self, constraint: Constraint) {
if self
.constraints
.iter()
.find(|&item| item == &constraint)
.is_some()
{
return;
}
// Check extra conditions for InTypeset constraints.
if let Constraint::InTypeset(tv, _) = &constraint {
assert!(tv.base.is_none());
assert!(tv.name.starts_with("typeof_"));
}
self.constraints.push(constraint);
}
/// Returns the canonical representative of the equivalency class of the given argument, or
/// duplicates it if it's not there yet.
pub fn get_equivalent(&self, tv: &TypeVar) -> TypeVar {
let mut tv = tv;
while let Some(found) = self.equivalency_map.get(tv) {
tv = found;
}
match &tv.base {
Some(parent) => self
.get_equivalent(&parent.type_var)
.derived(parent.derived_func),
None => tv.clone(),
}
}
/// Get the rank of tv in the partial order:
/// - TVs directly associated with a Var get their rank from the Var (see register()).
/// - Internally generated non-derived TVs implicitly get the lowest rank (0).
/// - Derived variables get their rank from their free typevar.
/// - Singletons have the highest rank.
/// - TVs associated with vars in a source pattern have a higher rank than TVs associated with
/// temporary vars.
fn rank(&self, tv: &TypeVar) -> u8 {
let actual_tv = match tv.base {
Some(_) => tv.free_typevar(),
None => Some(tv.clone()),
};
let rank = match actual_tv {
Some(actual_tv) => match self.ranks.get(&actual_tv) {
Some(rank) => Some(*rank),
None => {
assert!(
!actual_tv.name.starts_with("typeof_"),
format!("variable {} should be explicitly ranked", actual_tv.name)
);
None
}
},
None => None,
};
let rank = match rank {
Some(rank) => rank,
None => {
if tv.singleton_type().is_some() {
TypeEnvRank::Singleton
} else {
TypeEnvRank::Internal
}
}
};
rank as u8
}
/// Record the fact that the free tv1 is part of the same equivalence class as tv2. The
/// canonical representative of the merged class is tv2's canonical representative.
fn record_equivalent(&mut self, tv1: TypeVar, tv2: TypeVar) {
assert!(tv1.base.is_none());
assert!(self.get_equivalent(&tv1) == tv1);
if let Some(tv2_base) = &tv2.base {
// Ensure there are no cycles.
assert!(self.get_equivalent(&tv2_base.type_var) != tv1);
}
self.equivalency_map.insert(tv1, tv2);
}
/// Get the free typevars in the current type environment.
pub fn free_typevars(&self, var_pool: &mut VarPool) -> Vec<TypeVar> {
let mut typevars = Vec::new();
typevars.extend(self.equivalency_map.keys().cloned());
typevars.extend(
self.vars
.iter()
.map(|&var_index| var_pool.get_mut(var_index).get_or_create_typevar()),
);
let set: HashSet<TypeVar> = HashSet::from_iter(
typevars
.iter()
.map(|tv| self.get_equivalent(tv).free_typevar())
.filter(|opt_tv| {
// Filter out singleton types.
return opt_tv.is_some();
})
.map(|tv| tv.unwrap()),
);
Vec::from_iter(set)
}
/// Normalize by collapsing any roots that don't correspond to a concrete type var AND have a
/// single type var derived from them or equivalent to them.
///
/// e.g. if we have a root of the tree that looks like:
///
/// typeof_a typeof_b
/// \\ /
/// typeof_x
/// |
/// half_width(1)
/// |
/// 1
///
/// we want to collapse the linear path between 1 and typeof_x. The resulting graph is:
///
/// typeof_a typeof_b
/// \\ /
/// typeof_x
fn normalize(&mut self, var_pool: &mut VarPool) {
let source_tvs: HashSet<TypeVar> = HashSet::from_iter(
self.vars
.iter()
.map(|&var_index| var_pool.get_mut(var_index).get_or_create_typevar()),
);
let mut children: HashMap<TypeVar, HashSet<TypeVar>> = HashMap::new();
// Insert all the parents found by the derivation relationship.
for type_var in self.equivalency_map.values() {
if type_var.base.is_none() {
continue;
}
let parent_tv = type_var.free_typevar();
if parent_tv.is_none() {
// Ignore this type variable, it's a singleton.
continue;
}
let parent_tv = parent_tv.unwrap();
children
.entry(parent_tv)
.or_insert(HashSet::new())
.insert(type_var.clone());
}
// Insert all the explicit equivalency links.
for (equivalent_tv, canon_tv) in self.equivalency_map.iter() {
children
.entry(canon_tv.clone())
.or_insert(HashSet::new())
.insert(equivalent_tv.clone());
}
// Remove links that are straight paths up to typevar of variables.
for free_root in self.free_typevars(var_pool) {
let mut root = &free_root;
while !source_tvs.contains(&root)
&& children.contains_key(&root)
&& children.get(&root).unwrap().len() == 1
{
let child = children.get(&root).unwrap().iter().next().unwrap();
assert_eq!(self.equivalency_map[child], root.clone());
self.equivalency_map.remove(child);
root = child;
}
}
}
/// Extract a clean type environment from self, that only mentions type vars associated with
/// real variables.
fn extract(self, var_pool: &mut VarPool) -> TypeEnvironment {
let vars_tv: HashSet<TypeVar> = HashSet::from_iter(
self.vars
.iter()
.map(|&var_index| var_pool.get_mut(var_index).get_or_create_typevar()),
);
let mut new_equivalency_map: HashMap<TypeVar, TypeVar> = HashMap::new();
for tv in &vars_tv {
let canon_tv = self.get_equivalent(tv);
if *tv != canon_tv {
new_equivalency_map.insert(tv.clone(), canon_tv.clone());
}
// Sanity check: the translated type map should only refer to real variables.
assert!(vars_tv.contains(tv));
let canon_free_tv = canon_tv.free_typevar();
assert!(canon_free_tv.is_none() || vars_tv.contains(&canon_free_tv.unwrap()));
}
let mut new_constraints: HashSet<Constraint> = HashSet::new();
for constraint in &self.constraints {
let constraint = constraint.translate_with_env(&self);
if constraint.is_trivial() || new_constraints.contains(&constraint) {
continue;
}
// Sanity check: translated constraints should refer only to real variables.
for arg in constraint.typevar_args() {
assert!(vars_tv.contains(arg));
let arg_free_tv = arg.free_typevar();
assert!(arg_free_tv.is_none() || vars_tv.contains(&arg_free_tv.unwrap()));
}
new_constraints.insert(constraint);
}
TypeEnvironment {
vars: self.vars,
ranks: self.ranks,
equivalency_map: new_equivalency_map,
constraints: Vec::from_iter(new_constraints),
}
}
}
/// Replaces an external type variable according to the following rules:
/// - if a local copy is present in the map, return it.
/// - or if it's derived, create a local derived one that recursively substitutes the parent.
/// - or return itself.
fn substitute(map: &HashMap<&TypeVar, TypeVar>, external_type_var: &TypeVar) -> TypeVar {
match map.get(&external_type_var) {
Some(own_type_var) => own_type_var.clone(),
None => match &external_type_var.base {
Some(parent) => {
let parent_substitute = substitute(map, &parent.type_var);
TypeVar::derived(&parent_substitute, parent.derived_func)
}
None => external_type_var.clone(),
},
}
}
/// Normalize a (potentially derived) typevar using the following rules:
///
/// - vector and width derived functions commute
/// {HALF,DOUBLE}VECTOR({HALF,DOUBLE}WIDTH(base)) ->
/// {HALF,DOUBLE}WIDTH({HALF,DOUBLE}VECTOR(base))
///
/// - half/double pairs collapse
/// {HALF,DOUBLE}WIDTH({DOUBLE,HALF}WIDTH(base)) -> base
/// {HALF,DOUBLE}VECTOR({DOUBLE,HALF}VECTOR(base)) -> base
fn canonicalize_derivations(tv: TypeVar) -> TypeVar {
let base = match &tv.base {
Some(base) => base,
None => return tv,
};
let derived_func = base.derived_func;
if let Some(base_base) = &base.type_var.base {
let base_base_tv = &base_base.type_var;
match (derived_func, base_base.derived_func) {
(DerivedFunc::HalfWidth, DerivedFunc::DoubleWidth)
| (DerivedFunc::DoubleWidth, DerivedFunc::HalfWidth)
| (DerivedFunc::HalfVector, DerivedFunc::DoubleVector)
| (DerivedFunc::DoubleVector, DerivedFunc::HalfVector) => {
// Cancelling bijective transformations. This doesn't hide any overflow issues
// since derived type sets are checked upon derivaion, and base typesets are only
// allowed to shrink.
return canonicalize_derivations(base_base_tv.clone());
}
(DerivedFunc::HalfWidth, DerivedFunc::HalfVector)
| (DerivedFunc::HalfWidth, DerivedFunc::DoubleVector)
| (DerivedFunc::DoubleWidth, DerivedFunc::DoubleVector)
| (DerivedFunc::DoubleWidth, DerivedFunc::HalfVector) => {
// Arbitrarily put WIDTH derivations before VECTOR derivations, since they commute.
return canonicalize_derivations(
base_base_tv
.derived(derived_func)
.derived(base_base.derived_func),
);
}
_ => {}
};
}
canonicalize_derivations(base.type_var.clone()).derived(derived_func)
}
/// Given typevars tv1 and tv2 (which could be derived from one another), constrain their typesets
/// to be the same. When one is derived from the other, repeat the constrain process until
/// a fixed point is reached.
fn constrain_fixpoint(tv1: &TypeVar, tv2: &TypeVar) {
loop {
let old_tv1_ts = tv1.get_typeset().clone();
tv2.constrain_types(tv1.clone());
if tv1.get_typeset() == old_tv1_ts {
break;
}
}
let old_tv2_ts = tv2.get_typeset().clone();
tv1.constrain_types(tv2.clone());
// The above loop should ensure that all reference cycles have been handled.
assert!(old_tv2_ts == tv2.get_typeset());
}
/// Unify tv1 and tv2 in the given type environment. tv1 must have a rank greater or equal to tv2's
/// one, modulo commutations.
fn unify(tv1: &TypeVar, tv2: &TypeVar, type_env: &mut TypeEnvironment) -> Result<(), String> {
let tv1 = canonicalize_derivations(type_env.get_equivalent(tv1));
let tv2 = canonicalize_derivations(type_env.get_equivalent(tv2));
if tv1 == tv2 {
// Already unified.
return Ok(());
}
if type_env.rank(&tv2) < type_env.rank(&tv1) {
// Make sure tv1 always has the smallest rank, since real variables have the higher rank
// and we want them to be the canonical representatives of their equivalency classes.
return unify(&tv2, &tv1, type_env);
}
constrain_fixpoint(&tv1, &tv2);
if tv1.get_typeset().size() == 0 || tv2.get_typeset().size() == 0 {
return Err(format!(
"Error: empty type created when unifying {} and {}",
tv1.name, tv2.name
));
}
let base = match &tv1.base {
Some(base) => base,
None => {
type_env.record_equivalent(tv1, tv2);
return Ok(());
}
};
if let Some(inverse) = base.derived_func.inverse() {
return unify(&base.type_var, &tv2.derived(inverse), type_env);
}
type_env.add_constraint(Constraint::Eq(tv1, tv2));
Ok(())
}
/// Perform type inference on one Def in the current type environment and return an updated type
/// environment or error.
///
/// At a high level this works by creating fresh copies of each formal type var in the Def's
/// instruction's signature, and unifying the formal typevar with the corresponding actual typevar.
fn infer_definition(
def: &Def,
var_pool: &mut VarPool,
type_env: TypeEnvironment,
last_type_index: &mut usize,
) -> Result<TypeEnvironment, String> {
let apply = &def.apply;
let inst = &apply.inst;
let mut type_env = type_env;
let free_formal_tvs = inst.all_typevars();
let mut original_to_own_typevar: HashMap<&TypeVar, TypeVar> = HashMap::new();
for &tv in &free_formal_tvs {
assert!(original_to_own_typevar
.insert(
tv,
TypeVar::copy_from(tv, format!("own_{}", last_type_index))
)
.is_none());
*last_type_index += 1;
}
// Update the mapping with any explicity bound type vars:
for (i, value_type) in apply.value_types.iter().enumerate() {
let singleton = TypeVar::new_singleton(value_type.clone());
assert!(original_to_own_typevar
.insert(free_formal_tvs[i], singleton)
.is_some());
}
// Get fresh copies for each typevar in the signature (both free and derived).
let mut formal_tvs = Vec::new();
formal_tvs.extend(inst.value_results.iter().map(|&i| {
substitute(
&original_to_own_typevar,
inst.operands_out[i].type_var().unwrap(),
)
}));
formal_tvs.extend(inst.value_opnums.iter().map(|&i| {
substitute(
&original_to_own_typevar,
inst.operands_in[i].type_var().unwrap(),
)
}));
// Get the list of actual vars.
let mut actual_vars = Vec::new();
actual_vars.extend(inst.value_results.iter().map(|&i| def.defined_vars[i]));
actual_vars.extend(
inst.value_opnums
.iter()
.map(|&i| apply.args[i].unwrap_var()),
);
// Get the list of the actual TypeVars.
let mut actual_tvs = Vec::new();
for var_index in actual_vars {
let var = var_pool.get_mut(var_index);
type_env.register(var_index, var);
actual_tvs.push(var.get_or_create_typevar());
}
// Make sure we start unifying with the control type variable first, by putting it at the
// front of both vectors.
if let Some(poly) = &inst.polymorphic_info {
let own_ctrl_tv = &original_to_own_typevar[&poly.ctrl_typevar];
let ctrl_index = formal_tvs.iter().position(|tv| tv == own_ctrl_tv).unwrap();
if ctrl_index != 0 {
formal_tvs.swap(0, ctrl_index);
actual_tvs.swap(0, ctrl_index);
}
}
// Unify each actual type variable with the corresponding formal type variable.
for (actual_tv, formal_tv) in actual_tvs.iter().zip(&formal_tvs) {
if let Err(msg) = unify(actual_tv, formal_tv, &mut type_env) {
return Err(format!(
"fail ti on {} <: {}: {}",
actual_tv.name, formal_tv.name, msg
));
}
}
// Add any instruction specific constraints.
for constraint in &inst.constraints {
type_env.add_constraint(constraint.translate_with_map(&original_to_own_typevar));
}
Ok(type_env)
}
/// Perform type inference on an transformation. Return an updated type environment or error.
pub fn infer_transform(
src: DefIndex,
dst: &Vec<DefIndex>,
def_pool: &DefPool,
var_pool: &mut VarPool,
) -> Result<TypeEnvironment, String> {
let mut type_env = TypeEnvironment::new();
let mut last_type_index = 0;
// Execute type inference on the source pattern.
type_env = infer_definition(def_pool.get(src), var_pool, type_env, &mut last_type_index)
.map_err(|err| format!("In src pattern: {}", err))?;
// Collect the type sets once after applying the source patterm; we'll compare the typesets
// after we've also considered the destination pattern, and will emit supplementary InTypeset
// checks if they don't match.
let src_typesets = type_env
.vars
.iter()
.map(|&var_index| {
let var = var_pool.get_mut(var_index);
let tv = type_env.get_equivalent(&var.get_or_create_typevar());
(var_index, tv.get_typeset().clone())
})
.collect::<Vec<_>>();
// Execute type inference on the destination pattern.
for (i, &def_index) in dst.iter().enumerate() {
let def = def_pool.get(def_index);
type_env = infer_definition(def, var_pool, type_env, &mut last_type_index)
.map_err(|err| format!("line {}: {}", i, err))?;
}
for (var_index, src_typeset) in src_typesets {
let var = var_pool.get(var_index);
if !var.has_free_typevar() {
continue;
}
let tv = type_env.get_equivalent(&var.get_typevar().unwrap());
let new_typeset = tv.get_typeset();
assert!(
new_typeset.is_subset(&src_typeset),
"type sets can only get narrower"
);
if new_typeset != src_typeset {
type_env.add_constraint(Constraint::InTypeset(tv.clone(), new_typeset.clone()));
}
}
type_env.normalize(var_pool);
Ok(type_env.extract(var_pool))
}

View File

@ -1,7 +1,4 @@
use std::cell::RefCell;
use std::collections::{BTreeSet, HashSet};
use std::fmt;
use std::hash;
use std::collections::BTreeSet;
use std::iter::FromIterator;
use std::ops;
use std::rc::Rc;
@ -30,25 +27,25 @@ pub struct TypeVarContent {
/// Type set associated to the type variable.
/// This field must remain private; use `get_typeset()` or `get_raw_typeset()` to get the
/// information you want.
type_set: TypeSet,
type_set: Rc<TypeSet>,
pub base: Option<TypeVarParent>,
}
#[derive(Clone, Debug)]
pub struct TypeVar {
content: Rc<RefCell<TypeVarContent>>,
content: Rc<TypeVarContent>,
}
impl TypeVar {
pub fn new(name: impl Into<String>, doc: impl Into<String>, type_set: TypeSet) -> Self {
Self {
content: Rc::new(RefCell::new(TypeVarContent {
content: Rc::new(TypeVarContent {
name: name.into(),
doc: doc.into(),
type_set,
type_set: Rc::new(type_set),
base: None,
})),
}),
}
}
@ -89,37 +86,20 @@ impl TypeVar {
TypeVar::new(name, doc, builder.finish())
}
/// Get a fresh copy of self, named after `name`. Can only be called on non-derived typevars.
pub fn copy_from(other: &TypeVar, name: String) -> TypeVar {
assert!(
other.base.is_none(),
"copy_from() can only be called on non-derived type variables"
);
TypeVar {
content: Rc::new(RefCell::new(TypeVarContent {
name,
doc: "".into(),
type_set: other.type_set.clone(),
base: None,
})),
}
}
/// Returns the typeset for this TV. If the TV is derived, computes it recursively from the
/// derived function and the base's typeset.
/// Note this can't be done non-lazily in the constructor, because the TypeSet of the base may
/// change over time.
pub fn get_typeset(&self) -> TypeSet {
match &self.base {
Some(base) => base.type_var.get_typeset().image(base.derived_func),
None => self.type_set.clone(),
/// Returns this typevar's type set, maybe computing it from the parent.
fn get_typeset(&self) -> Rc<TypeSet> {
// TODO Can this be done in a non-lazy way in derived() and we can remove this function and
// the one below?
match &self.content.base {
Some(base) => Rc::new(base.type_var.get_typeset().image(base.derived_func)),
None => self.content.type_set.clone(),
}
}
/// Returns this typevar's type set, assuming this type var has no parent.
pub fn get_raw_typeset(&self) -> &TypeSet {
assert_eq!(self.type_set, self.get_typeset());
&self.type_set
assert_eq!(self.content.type_set, self.get_typeset());
&*self.content.type_set
}
/// If the associated typeset has a single type return it. Otherwise return None.
@ -134,7 +114,7 @@ impl TypeVar {
/// Get the free type variable controlling this one.
pub fn free_typevar(&self) -> Option<TypeVar> {
match &self.base {
match &self.content.base {
Some(base) => base.type_var.free_typevar(),
None => {
match self.singleton_type() {
@ -147,7 +127,7 @@ impl TypeVar {
}
/// Create a type variable that is a function of another.
pub fn derived(&self, derived_func: DerivedFunc) -> TypeVar {
fn derived(&self, derived_func: DerivedFunc) -> TypeVar {
let ts = self.get_typeset();
// Safety checks to avoid over/underflows.
@ -199,7 +179,7 @@ impl TypeVar {
}
return TypeVar {
content: Rc::new(RefCell::new(TypeVarContent {
content: Rc::new(TypeVarContent {
name: format!("{}({})", derived_func.name(), self.name),
doc: "".into(),
type_set: ts,
@ -207,7 +187,7 @@ impl TypeVar {
type_var: self.clone(),
derived_func,
}),
})),
}),
};
}
@ -232,52 +212,6 @@ impl TypeVar {
pub fn to_bitvec(&self) -> TypeVar {
return self.derived(DerivedFunc::ToBitVec);
}
/// Constrain the range of types this variable can assume to a subset of those in the typeset
/// ts.
/// May mutate itself if it's not derived, or its parent if it is.
pub fn constrain_types_by_ts(&self, type_set: TypeSet) {
match &self.base {
Some(base) => {
base.type_var
.constrain_types_by_ts(type_set.preimage(base.derived_func));
}
None => {
self.content
.borrow_mut()
.type_set
.inplace_intersect_with(&type_set);
}
}
}
/// Constrain the range of types this variable can assume to a subset of those `other` can
/// assume.
/// May mutate itself if it's not derived, or its parent if it is.
pub fn constrain_types(&self, other: TypeVar) {
if self == &other {
return;
}
self.constrain_types_by_ts(other.get_typeset());
}
/// Get a Rust expression that computes the type of this type variable.
pub fn to_rust_code(&self) -> String {
match &self.base {
Some(base) => format!(
"{}.{}()",
base.type_var.to_rust_code(),
base.derived_func.name()
),
None => {
if let Some(singleton) = self.singleton_type() {
singleton.rust_name()
} else {
self.name.clone()
}
}
}
}
}
impl Into<TypeVar> for &TypeVar {
@ -291,46 +225,24 @@ impl Into<TypeVar> for ValueType {
}
}
// Hash TypeVars by pointers.
// There might be a better way to do this, but since TypeVar's content (namely TypeSet) can be
// mutated, it makes sense to use pointer equality/hashing here.
impl hash::Hash for TypeVar {
fn hash<H: hash::Hasher>(&self, h: &mut H) {
match &self.base {
Some(base) => {
base.type_var.hash(h);
base.derived_func.hash(h);
}
None => {
(&**self as *const TypeVarContent).hash(h);
}
}
}
}
impl PartialEq for TypeVar {
fn eq(&self, other: &TypeVar) -> bool {
match (&self.base, &other.base) {
(Some(base1), Some(base2)) => {
base1.type_var.eq(&base2.type_var) && base1.derived_func == base2.derived_func
}
match (&self.content.base, &other.content.base) {
(Some(base1), Some(base2)) => base1.type_var.eq(&base2.type_var),
(None, None) => Rc::ptr_eq(&self.content, &other.content),
_ => false,
}
}
}
// Allow TypeVar as map keys, based on pointer equality (see also above PartialEq impl).
impl Eq for TypeVar {}
impl ops::Deref for TypeVar {
type Target = TypeVarContent;
fn deref(&self) -> &Self::Target {
unsafe { self.content.as_ptr().as_ref().unwrap() }
&*self.content
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq)]
#[derive(Clone, Copy, Debug)]
pub enum DerivedFunc {
LaneOf,
AsBool,
@ -353,20 +265,9 @@ impl DerivedFunc {
DerivedFunc::ToBitVec => "to_bitvec",
}
}
/// Returns the inverse function of this one, if it is a bijection.
pub fn inverse(&self) -> Option<DerivedFunc> {
match self {
DerivedFunc::HalfWidth => Some(DerivedFunc::DoubleWidth),
DerivedFunc::DoubleWidth => Some(DerivedFunc::HalfWidth),
DerivedFunc::HalfVector => Some(DerivedFunc::DoubleVector),
DerivedFunc::DoubleVector => Some(DerivedFunc::HalfVector),
_ => None,
}
}
}
#[derive(Debug, Hash)]
#[derive(Debug)]
pub struct TypeVarParent {
pub type_var: TypeVar,
pub derived_func: DerivedFunc,
@ -400,7 +301,7 @@ macro_rules! num_set {
};
}
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TypeSet {
pub lanes: NumSet,
pub ints: NumSet,
@ -430,7 +331,7 @@ impl TypeSet {
}
/// Return the number of concrete types represented by this typeset.
pub fn size(&self) -> usize {
fn size(&self) -> usize {
self.lanes.len()
* (self.ints.len() + self.floats.len() + self.bools.len() + self.bitvecs.len())
+ self.specials.len()
@ -585,175 +486,6 @@ impl TypeSet {
assert_eq!(types.len(), 1);
return types.remove(0);
}
/// Return the inverse image of self across the derived function func.
fn preimage(&self, func: DerivedFunc) -> TypeSet {
if self.size() == 0 {
// The inverse of the empty set is itself.
return self.clone();
}
match func {
DerivedFunc::LaneOf => {
let mut copy = self.clone();
copy.bitvecs = NumSet::new();
copy.lanes =
NumSet::from_iter((0..MAX_LANES.trailing_zeros() + 1).map(|i| u16::pow(2, i)));
copy
}
DerivedFunc::AsBool => {
let mut copy = self.clone();
copy.bitvecs = NumSet::new();
if self.bools.contains(&1) {
copy.ints = NumSet::from_iter(vec![8, 16, 32, 64]);
copy.floats = NumSet::from_iter(vec![32, 64]);
} else {
copy.ints = &self.bools - &NumSet::from_iter(vec![1]);
copy.floats = &self.bools & &NumSet::from_iter(vec![32, 64]);
// If b1 is not in our typeset, than lanes=1 cannot be in the pre-image, as
// as_bool() of scalars is always b1.
copy.lanes = &self.lanes - &NumSet::from_iter(vec![1]);
}
copy
}
DerivedFunc::HalfWidth => self.double_width(),
DerivedFunc::DoubleWidth => self.half_width(),
DerivedFunc::HalfVector => self.double_vector(),
DerivedFunc::DoubleVector => self.half_vector(),
DerivedFunc::ToBitVec => {
let all_lanes = range_to_set(Some(1..MAX_LANES));
let all_ints = range_to_set(Some(8..MAX_BITS));
let all_floats = range_to_set(Some(32..64));
let all_bools = range_to_set(Some(1..MAX_BITS));
let mut lanes = range_to_set(Some(1..MAX_LANES));
let mut ints = range_to_set(Some(8..MAX_BITS));
let mut floats = range_to_set(Some(32..64));
let mut bools = range_to_set(Some(1..MAX_BITS));
for &l in &all_lanes {
for &i in &all_ints {
if self.bitvecs.contains(&(i * l)) {
lanes.insert(l);
ints.insert(i);
}
}
for &f in &all_floats {
if self.bitvecs.contains(&(f * l)) {
lanes.insert(l);
floats.insert(f);
}
}
for &b in &all_bools {
if self.bitvecs.contains(&(b * l)) {
lanes.insert(l);
bools.insert(b);
}
}
}
let bitvecs = NumSet::new();
let specials = Vec::new();
TypeSet::new(lanes, ints, floats, bools, bitvecs, specials)
}
}
}
pub fn inplace_intersect_with(&mut self, other: &TypeSet) {
self.lanes = &self.lanes & &other.lanes;
self.ints = &self.ints & &other.ints;
self.floats = &self.floats & &other.floats;
self.bools = &self.bools & &other.bools;
self.bitvecs = &self.bitvecs & &other.bitvecs;
let mut new_specials = Vec::new();
for spec in &self.specials {
if let Some(spec) = other.specials.iter().find(|&other_spec| other_spec == spec) {
new_specials.push(*spec);
}
}
self.specials = new_specials;
}
pub fn is_subset(&self, other: &TypeSet) -> bool {
self.lanes.is_subset(&other.lanes)
&& self.ints.is_subset(&other.ints)
&& self.floats.is_subset(&other.floats)
&& self.bools.is_subset(&other.bools)
&& self.bitvecs.is_subset(&other.bitvecs)
&& {
let specials: HashSet<SpecialType> = HashSet::from_iter(self.specials.clone());
let other_specials = HashSet::from_iter(other.specials.clone());
specials.is_subset(&other_specials)
}
}
pub fn is_wider_or_equal(&self, other: &TypeSet) -> bool {
set_wider_or_equal(&self.ints, &other.ints)
&& set_wider_or_equal(&self.floats, &other.floats)
&& set_wider_or_equal(&self.bools, &other.bools)
}
pub fn is_narrower(&self, other: &TypeSet) -> bool {
set_narrower(&self.ints, &other.ints)
&& set_narrower(&self.floats, &other.floats)
&& set_narrower(&self.bools, &other.bools)
}
}
fn set_wider_or_equal(s1: &NumSet, s2: &NumSet) -> bool {
s1.len() > 0 && s2.len() > 0 && s1.iter().min() >= s2.iter().max()
}
fn set_narrower(s1: &NumSet, s2: &NumSet) -> bool {
s1.len() > 0 && s2.len() > 0 && s1.iter().min() < s2.iter().max()
}
impl fmt::Debug for TypeSet {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "TypeSet(")?;
let mut subsets = Vec::new();
if !self.lanes.is_empty() {
subsets.push(format!(
"lanes={{{}}}",
Vec::from_iter(self.lanes.iter().map(|x| x.to_string())).join(", ")
));
}
if !self.ints.is_empty() {
subsets.push(format!(
"ints={{{}}}",
Vec::from_iter(self.ints.iter().map(|x| x.to_string())).join(", ")
));
}
if !self.floats.is_empty() {
subsets.push(format!(
"floats={{{}}}",
Vec::from_iter(self.floats.iter().map(|x| x.to_string())).join(", ")
));
}
if !self.bools.is_empty() {
subsets.push(format!(
"bools={{{}}}",
Vec::from_iter(self.bools.iter().map(|x| x.to_string())).join(", ")
));
}
if !self.bitvecs.is_empty() {
subsets.push(format!(
"bitvecs={{{}}}",
Vec::from_iter(self.bitvecs.iter().map(|x| x.to_string())).join(", ")
));
}
if !self.specials.is_empty() {
subsets.push(format!(
"specials={{{}}}",
Vec::from_iter(self.specials.iter().map(|x| x.to_string())).join(", ")
));
}
write!(fmt, "{})", subsets.join(", "))?;
Ok(())
}
}
pub struct TypeSetBuilder {
@ -831,18 +563,6 @@ impl TypeSetBuilder {
self.specials,
)
}
pub fn all() -> TypeSet {
TypeSetBuilder::new()
.ints(Interval::All)
.floats(Interval::All)
.bools(Interval::All)
.simd_lanes(Interval::All)
.bitvecs(Interval::All)
.specials(ValueType::all_special_types().collect())
.includes_scalars(true)
.finish()
}
}
#[derive(PartialEq)]
@ -1085,136 +805,6 @@ fn test_forward_images() {
);
}
#[test]
fn test_backward_images() {
let empty_set = TypeSetBuilder::new().finish();
// LaneOf.
assert_eq!(
TypeSetBuilder::new()
.simd_lanes(1..1)
.ints(8..8)
.floats(32..32)
.finish()
.preimage(DerivedFunc::LaneOf),
TypeSetBuilder::new()
.simd_lanes(Interval::All)
.ints(8..8)
.floats(32..32)
.finish()
);
assert_eq!(empty_set.preimage(DerivedFunc::LaneOf), empty_set);
// AsBool.
assert_eq!(
TypeSetBuilder::new()
.simd_lanes(1..4)
.bools(1..64)
.finish()
.preimage(DerivedFunc::AsBool),
TypeSetBuilder::new()
.simd_lanes(1..4)
.ints(Interval::All)
.bools(Interval::All)
.floats(Interval::All)
.finish()
);
// Double vector.
assert_eq!(
TypeSetBuilder::new()
.simd_lanes(1..1)
.ints(8..8)
.finish()
.preimage(DerivedFunc::DoubleVector)
.size(),
0
);
assert_eq!(
TypeSetBuilder::new()
.simd_lanes(1..16)
.ints(8..16)
.floats(32..32)
.finish()
.preimage(DerivedFunc::DoubleVector),
TypeSetBuilder::new()
.simd_lanes(1..8)
.ints(8..16)
.floats(32..32)
.finish(),
);
// Half vector.
assert_eq!(
TypeSetBuilder::new()
.simd_lanes(256..256)
.ints(8..8)
.finish()
.preimage(DerivedFunc::HalfVector)
.size(),
0
);
assert_eq!(
TypeSetBuilder::new()
.simd_lanes(64..128)
.bools(1..32)
.finish()
.preimage(DerivedFunc::HalfVector),
TypeSetBuilder::new()
.simd_lanes(128..256)
.bools(1..32)
.finish(),
);
// Half width.
assert_eq!(
TypeSetBuilder::new()
.ints(64..64)
.floats(64..64)
.bools(64..64)
.finish()
.preimage(DerivedFunc::HalfWidth)
.size(),
0
);
assert_eq!(
TypeSetBuilder::new()
.simd_lanes(64..256)
.bools(1..64)
.finish()
.preimage(DerivedFunc::HalfWidth),
TypeSetBuilder::new()
.simd_lanes(64..256)
.bools(16..64)
.finish(),
);
// Double width.
assert_eq!(
TypeSetBuilder::new()
.ints(8..8)
.floats(32..32)
.bools(1..8)
.finish()
.preimage(DerivedFunc::DoubleWidth)
.size(),
0
);
assert_eq!(
TypeSetBuilder::new()
.simd_lanes(1..16)
.ints(8..16)
.floats(32..64)
.finish()
.preimage(DerivedFunc::DoubleWidth),
TypeSetBuilder::new()
.simd_lanes(1..16)
.ints(8..8)
.floats(32..32)
.finish()
);
}
#[test]
#[should_panic]
fn test_typeset_singleton_panic_nonsingleton_types() {

View File

@ -1,416 +0,0 @@
use crate::cdsl::ast::{
Apply, DefIndex, DefPool, DummyDef, DummyExpr, Expr, PatternPosition, VarIndex, VarPool,
};
use crate::cdsl::inst::Instruction;
use crate::cdsl::type_inference::{infer_transform, TypeEnvironment};
use crate::cdsl::typevar::TypeVar;
use cranelift_entity::{entity_impl, PrimaryMap};
use std::collections::{HashMap, HashSet};
use std::iter::FromIterator;
/// An instruction transformation consists of a source and destination pattern.
///
/// Patterns are expressed in *register transfer language* as tuples of Def or Expr nodes. A
/// pattern may optionally have a sequence of TypeConstraints, that additionally limit the set of
/// cases when it applies.
///
/// The source pattern can contain only a single instruction.
pub struct Transform {
pub src: DefIndex,
pub dst: Vec<DefIndex>,
pub var_pool: VarPool,
pub def_pool: DefPool,
pub type_env: TypeEnvironment,
}
type SymbolTable = HashMap<&'static str, VarIndex>;
impl Transform {
fn new(src: DummyDef, dst: Vec<DummyDef>) -> Self {
let mut var_pool = VarPool::new();
let mut def_pool = DefPool::new();
let mut input_vars: Vec<VarIndex> = Vec::new();
let mut defined_vars: Vec<VarIndex> = Vec::new();
// Maps variable names to our own Var copies.
let mut symbol_table: SymbolTable = SymbolTable::new();
// Rewrite variables in src and dst using our own copies.
let src = rewrite_def_list(
PatternPosition::Source,
vec![src],
&mut symbol_table,
&mut input_vars,
&mut defined_vars,
&mut var_pool,
&mut def_pool,
)[0];
let num_src_inputs = input_vars.len();
let dst = rewrite_def_list(
PatternPosition::Destination,
dst,
&mut symbol_table,
&mut input_vars,
&mut defined_vars,
&mut var_pool,
&mut def_pool,
);
// Sanity checks.
for &var_index in &input_vars {
assert!(
var_pool.get(var_index).is_input(),
format!("'{:?}' used as both input and def", var_pool.get(var_index))
);
}
assert!(
input_vars.len() == num_src_inputs,
format!(
"extra input vars in dst pattern: {:?}",
input_vars
.iter()
.map(|&i| var_pool.get(i))
.skip(num_src_inputs)
.collect::<Vec<_>>()
)
);
// Perform type inference and cleanup.
let type_env = infer_transform(src, &dst, &def_pool, &mut var_pool).unwrap();
// Sanity check: the set of inferred free type variables should be a subset of the type
// variables corresponding to Vars appearing in the source pattern.
{
let free_typevars: HashSet<TypeVar> =
HashSet::from_iter(type_env.free_typevars(&mut var_pool));
let src_tvs = HashSet::from_iter(
input_vars
.clone()
.iter()
.chain(
defined_vars
.iter()
.filter(|&&var_index| !var_pool.get(var_index).is_temp()),
)
.map(|&var_index| var_pool.get(var_index).get_typevar())
.filter(|maybe_var| maybe_var.is_some())
.map(|var| var.unwrap()),
);
if !free_typevars.is_subset(&src_tvs) {
let missing_tvs = (&free_typevars - &src_tvs)
.iter()
.map(|tv| tv.name.clone())
.collect::<Vec<_>>()
.join(", ");
panic!("Some free vars don't appear in src: {}", missing_tvs);
}
}
for &var_index in input_vars.iter().chain(defined_vars.iter()) {
let var = var_pool.get_mut(var_index);
let canon_tv = type_env.get_equivalent(&var.get_or_create_typevar());
var.set_typevar(canon_tv);
}
Self {
src,
dst,
var_pool,
def_pool,
type_env,
}
}
fn verify_legalize(&self) {
let def = self.def_pool.get(self.src);
for &var_index in def.defined_vars.iter() {
let defined_var = self.var_pool.get(var_index);
assert!(
defined_var.is_output(),
format!("{:?} not defined in the destination pattern", defined_var)
);
}
}
}
/// Given a list of symbols defined in a Def, rewrite them to local symbols. Yield the new locals.
fn rewrite_defined_vars(
position: PatternPosition,
dummy_def: &DummyDef,
def_index: DefIndex,
symbol_table: &mut SymbolTable,
defined_vars: &mut Vec<VarIndex>,
var_pool: &mut VarPool,
) -> Vec<VarIndex> {
let mut new_defined_vars = Vec::new();
for var in &dummy_def.defined_vars {
let own_var = match symbol_table.get(var.name) {
Some(&existing_var) => existing_var,
None => {
// Materialize the variable.
let new_var = var_pool.create(var.name);
symbol_table.insert(var.name, new_var);
defined_vars.push(new_var);
new_var
}
};
var_pool.get_mut(own_var).set_def(position, def_index);
new_defined_vars.push(own_var);
}
new_defined_vars
}
/// Find all uses of variables in `expr` and replace them with our own local symbols.
fn rewrite_expr(
position: PatternPosition,
dummy_expr: DummyExpr,
symbol_table: &mut SymbolTable,
input_vars: &mut Vec<VarIndex>,
var_pool: &mut VarPool,
) -> Apply {
let (apply_target, dummy_args) = if let DummyExpr::Apply(apply_target, dummy_args) = dummy_expr
{
(apply_target, dummy_args)
} else {
panic!("we only rewrite apply expressions");
};
assert_eq!(
apply_target.inst().operands_in.len(),
dummy_args.len(),
"number of arguments in instruction is incorrect"
);
let mut args = Vec::new();
for (i, arg) in dummy_args.into_iter().enumerate() {
match arg {
DummyExpr::Var(var) => {
let own_var = match symbol_table.get(var.name) {
Some(&own_var) => {
let var = var_pool.get(own_var);
assert!(
var.is_input() || var.get_def(position).is_some(),
format!("{:?} used as both input and def", var)
);
own_var
}
None => {
// First time we're using this variable.
let own_var = var_pool.create(var.name);
symbol_table.insert(var.name, own_var);
input_vars.push(own_var);
own_var
}
};
args.push(Expr::Var(own_var));
}
DummyExpr::Literal(literal) => {
assert!(!apply_target.inst().operands_in[i].is_value());
args.push(Expr::Literal(literal));
}
DummyExpr::Apply(..) => {
panic!("Recursive apply is not allowed.");
}
}
}
Apply::new(apply_target, args)
}
fn rewrite_def_list(
position: PatternPosition,
dummy_defs: Vec<DummyDef>,
symbol_table: &mut SymbolTable,
input_vars: &mut Vec<VarIndex>,
defined_vars: &mut Vec<VarIndex>,
var_pool: &mut VarPool,
def_pool: &mut DefPool,
) -> Vec<DefIndex> {
let mut new_defs = Vec::new();
for dummy_def in dummy_defs {
let def_index = def_pool.next_index();
let new_defined_vars = rewrite_defined_vars(
position,
&dummy_def,
def_index,
symbol_table,
defined_vars,
var_pool,
);
let new_apply = rewrite_expr(position, dummy_def.expr, symbol_table, input_vars, var_pool);
assert!(
def_pool.next_index() == def_index,
"shouldn't have created new defs in the meanwhile"
);
assert_eq!(
new_apply.inst.value_results.len(),
new_defined_vars.len(),
"number of Var results in instruction is incorrect"
);
new_defs.push(def_pool.create(new_apply, new_defined_vars));
}
new_defs
}
/// A group of related transformations.
pub struct TransformGroup {
pub name: &'static str,
pub doc: &'static str,
pub chain_with: Option<TransformGroupIndex>,
pub isa_name: Option<&'static str>,
pub id: TransformGroupIndex,
/// Maps Instruction camel_case names to custom legalization functions names.
pub custom_legalizes: HashMap<String, &'static str>,
pub transforms: Vec<Transform>,
}
impl TransformGroup {
pub fn rust_name(&self) -> String {
match self.isa_name {
Some(_) => {
// This is a function in the same module as the LEGALIZE_ACTIONS table referring to
// it.
self.name.to_string()
}
None => format!("crate::legalizer::{}", self.name),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct TransformGroupIndex(u32);
entity_impl!(TransformGroupIndex);
pub struct TransformGroupBuilder {
name: &'static str,
doc: &'static str,
chain_with: Option<TransformGroupIndex>,
isa_name: Option<&'static str>,
pub custom_legalizes: HashMap<String, &'static str>,
pub transforms: Vec<Transform>,
}
impl TransformGroupBuilder {
pub fn new(name: &'static str, doc: &'static str) -> Self {
Self {
name,
doc,
chain_with: None,
isa_name: None,
custom_legalizes: HashMap::new(),
transforms: Vec::new(),
}
}
pub fn chain_with(mut self, next_id: TransformGroupIndex) -> Self {
assert!(self.chain_with.is_none());
self.chain_with = Some(next_id);
self
}
pub fn isa(mut self, isa_name: &'static str) -> Self {
assert!(self.isa_name.is_none());
self.isa_name = Some(isa_name);
self
}
/// Add a custom legalization action for `inst`.
///
/// The `func_name` parameter is the fully qualified name of a Rust function which takes the
/// same arguments as the `isa::Legalize` actions.
///
/// The custom function will be called to legalize `inst` and any return value is ignored.
pub fn custom_legalize(&mut self, inst: &Instruction, func_name: &'static str) {
assert!(
self.custom_legalizes
.insert(inst.camel_name.clone(), func_name)
.is_none(),
format!(
"custom legalization action for {} inserted twice",
inst.name
)
);
}
/// Add a legalization pattern to this group.
pub fn legalize(&mut self, src: DummyDef, dst: Vec<DummyDef>) {
let transform = Transform::new(src, dst);
transform.verify_legalize();
self.transforms.push(transform);
}
pub fn finish_and_add_to(self, owner: &mut TransformGroups) -> TransformGroupIndex {
let next_id = owner.next_key();
owner.add(TransformGroup {
name: self.name,
doc: self.doc,
isa_name: self.isa_name,
id: next_id,
chain_with: self.chain_with,
custom_legalizes: self.custom_legalizes,
transforms: self.transforms,
})
}
}
pub struct TransformGroups {
groups: PrimaryMap<TransformGroupIndex, TransformGroup>,
}
impl TransformGroups {
pub fn new() -> Self {
Self {
groups: PrimaryMap::new(),
}
}
pub fn add(&mut self, new_group: TransformGroup) -> TransformGroupIndex {
for group in self.groups.values() {
assert!(
group.name != new_group.name,
format!("trying to insert {} for the second time", new_group.name)
);
}
self.groups.push(new_group)
}
pub fn get(&self, id: TransformGroupIndex) -> &TransformGroup {
&self.groups[id]
}
pub fn get_mut(&mut self, id: TransformGroupIndex) -> &mut TransformGroup {
self.groups.get_mut(id).unwrap()
}
fn next_key(&self) -> TransformGroupIndex {
self.groups.next_key()
}
pub fn by_name(&self, name: &'static str) -> &TransformGroup {
for group in self.groups.values() {
if group.name == name {
return group;
}
}
panic!(format!("transform group with name {} not found", name));
}
}
#[test]
#[should_panic]
fn test_double_custom_legalization() {
use crate::cdsl::formats::{FormatRegistry, InstructionFormatBuilder};
use crate::cdsl::inst::InstructionBuilder;
let mut format = FormatRegistry::new();
format.insert(InstructionFormatBuilder::new("nullary"));
let dummy_inst = InstructionBuilder::new("dummy", "doc").finish(&format);
let mut transform_group = TransformGroupBuilder::new("test", "doc");
transform_group.custom_legalize(&dummy_inst, "custom 1");
transform_group.custom_legalize(&dummy_inst, "custom 2");
}

View File

@ -665,7 +665,7 @@ fn typeset_to_string(ts: &TypeSet) -> String {
}
/// Generate the table of ValueTypeSets described by type_sets.
pub fn gen_typesets_table(type_sets: &UniqueTable<TypeSet>, fmt: &mut Formatter) {
fn gen_typesets_table(type_sets: &UniqueTable<TypeSet>, fmt: &mut Formatter) {
if type_sets.len() == 0 {
return;
}

View File

@ -1,578 +0,0 @@
use crate::cdsl::ast::{Def, DefPool, VarPool};
use crate::cdsl::formats::FormatRegistry;
use crate::cdsl::isa::TargetIsa;
use crate::cdsl::type_inference::Constraint;
use crate::cdsl::typevar::{TypeSet, TypeVar};
use crate::cdsl::xform::{Transform, TransformGroup, TransformGroups};
use crate::error;
use crate::gen_inst::gen_typesets_table;
use crate::srcgen::Formatter;
use crate::unique_table::UniqueTable;
use std::collections::{HashMap, HashSet};
use std::iter::FromIterator;
/// Given a `Def` node, emit code that extracts all the instruction fields from
/// `pos.func.dfg[iref]`.
///
/// Create local variables named after the `Var` instances in `node`.
///
/// Also create a local variable named `predicate` with the value of the evaluated instruction
/// predicate, or `true` if the node has no predicate.
fn unwrap_inst(
transform: &Transform,
format_registry: &FormatRegistry,
fmt: &mut Formatter,
) -> bool {
let var_pool = &transform.var_pool;
let def_pool = &transform.def_pool;
let def = def_pool.get(transform.src);
let apply = &def.apply;
let inst = &apply.inst;
let iform = format_registry.get(inst.format);
fmt.comment(format!(
"Unwrap {}",
def.to_comment_string(&transform.var_pool)
));
// Extract the Var arguments.
let arg_names = apply
.args
.iter()
.map(|arg| match arg.maybe_var() {
Some(var_index) => var_pool.get(var_index).name,
None => "_",
})
.collect::<Vec<_>>()
.join(", ");
fmtln!(
fmt,
"let ({}, predicate) = if let crate::ir::InstructionData::{} {{",
arg_names,
iform.name
);
fmt.indent(|fmt| {
// Fields are encoded directly.
for field in &iform.imm_fields {
fmtln!(fmt, "{},", field.member);
}
if iform.num_value_operands == 1 {
fmt.line("arg,");
} else if iform.has_value_list || iform.num_value_operands > 1 {
fmt.line("ref args,");
}
fmt.line("..");
fmt.outdented_line("} = pos.func.dfg[inst] {");
fmt.line("let func = &pos.func;");
if iform.has_value_list {
fmt.line("let args = args.as_slice(&func.dfg.value_lists);");
} else if iform.num_value_operands == 1 {
fmt.line("let args = [arg];")
}
// Generate the values for the tuple.
fmt.line("(");
fmt.indent(|fmt| {
for (op_num, op) in inst.operands_in.iter().enumerate() {
if op.is_immediate() {
let n = inst.imm_opnums.iter().position(|&i| i == op_num).unwrap();
fmtln!(fmt, "{},", iform.imm_fields[n].member);
} else if op.is_value() {
let n = inst.value_opnums.iter().position(|&i| i == op_num).unwrap();
fmtln!(fmt, "func.dfg.resolve_aliases(args[{}]),", n);
}
}
// Evaluate the instruction predicate if any.
fmt.multi_line(
&apply
.inst_predicate_with_ctrl_typevar(format_registry, var_pool)
.rust_predicate(),
);
});
fmt.line(")");
fmt.outdented_line("} else {");
fmt.line(r#"unreachable!("bad instruction format")"#);
});
fmtln!(fmt, "};");
for &op_num in &inst.value_opnums {
let arg = &apply.args[op_num];
if let Some(var_index) = arg.maybe_var() {
let var = var_pool.get(var_index);
if var.has_free_typevar() {
fmtln!(
fmt,
"let typeof_{} = pos.func.dfg.value_type({});",
var.name,
var.name
);
}
}
}
// If the definition creates results, detach the values and place them in locals.
let mut replace_inst = false;
if def.defined_vars.len() > 0 {
if def.defined_vars
== def_pool
.get(var_pool.get(def.defined_vars[0]).dst_def.unwrap())
.defined_vars
{
// Special case: The instruction replacing node defines the exact same values.
fmt.comment(format!(
"Results handled by {}.",
def_pool
.get(var_pool.get(def.defined_vars[0]).dst_def.unwrap())
.to_comment_string(var_pool)
));
replace_inst = true;
} else {
// Boring case: Detach the result values, capture them in locals.
for &var_index in &def.defined_vars {
fmtln!(fmt, "let {};", var_pool.get(var_index).name);
}
fmt.line("{");
fmt.indent(|fmt| {
fmt.line("let r = pos.func.dfg.inst_results(inst);");
for i in 0..def.defined_vars.len() {
let var = var_pool.get(def.defined_vars[i]);
fmtln!(fmt, "{} = r[{}];", var.name, i);
}
});
fmt.line("}");
for &var_index in &def.defined_vars {
let var = var_pool.get(var_index);
if var.has_free_typevar() {
fmtln!(
fmt,
"let typeof_{} = pos.func.dfg.value_type({});",
var.name,
var.name
);
}
}
}
}
replace_inst
}
fn build_derived_expr(tv: &TypeVar) -> String {
let base = match &tv.base {
Some(base) => base,
None => {
assert!(tv.name.starts_with("typeof_"));
return format!("Some({})", tv.name);
}
};
let base_expr = build_derived_expr(&base.type_var);
format!(
"{}.map(|t: crate::ir::Type| t.{}())",
base_expr,
base.derived_func.name()
)
}
/// Emit rust code for the given check.
///
/// The emitted code is a statement redefining the `predicate` variable like this:
/// let predicate = predicate && ...
fn emit_runtime_typecheck<'a, 'b>(
constraint: &'a Constraint,
type_sets: &mut UniqueTable<'a, TypeSet>,
fmt: &mut Formatter,
) {
match constraint {
Constraint::InTypeset(tv, ts) => {
let ts_index = type_sets.add(&ts);
fmt.comment(format!(
"{} must belong to {:?}",
tv.name,
type_sets.get(ts_index)
));
fmtln!(
fmt,
"let predicate = predicate && TYPE_SETS[{}].contains({});",
ts_index,
tv.name
);
}
Constraint::Eq(tv1, tv2) => {
fmtln!(
fmt,
"let predicate = predicate && match ({}, {}) {{",
build_derived_expr(tv1),
build_derived_expr(tv2)
);
fmt.indent(|fmt| {
fmt.line("(Some(a), Some(b)) => a == b,");
fmt.comment("On overflow, constraint doesn\'t apply");
fmt.line("_ => false,");
});
fmtln!(fmt, "};");
}
Constraint::WiderOrEq(tv1, tv2) => {
fmtln!(
fmt,
"let predicate = predicate && match ({}, {}) {{",
build_derived_expr(tv1),
build_derived_expr(tv2)
);
fmt.indent(|fmt| {
fmt.line("(Some(a), Some(b)) => a.wider_or_equal(b),");
fmt.comment("On overflow, constraint doesn\'t apply");
fmt.line("_ => false,");
});
fmtln!(fmt, "};");
}
}
}
/// Determine if `node` represents one of the value splitting instructions: `isplit` or `vsplit.
/// These instructions are lowered specially by the `legalize::split` module.
fn is_value_split(def: &Def) -> bool {
let name = def.apply.inst.name;
name == "isplit" || name == "vsplit"
}
fn emit_dst_inst(def: &Def, def_pool: &DefPool, var_pool: &VarPool, fmt: &mut Formatter) {
let defined_vars = {
let vars = def
.defined_vars
.iter()
.map(|&var_index| var_pool.get(var_index).name)
.collect::<Vec<_>>();
if vars.len() == 1 {
vars[0].to_string()
} else {
format!("({})", vars.join(", "))
}
};
if is_value_split(def) {
// Split instructions are not emitted with the builder, but by calling special functions in
// the `legalizer::split` module. These functions will eliminate concat-split patterns.
fmt.line("let curpos = pos.position();");
fmt.line("let srcloc = pos.srcloc();");
fmtln!(
fmt,
"let {} = split::{}(pos.func, cfg, curpos, srcloc, {});",
defined_vars,
def.apply.inst.snake_name(),
def.apply.args[0].to_rust_code(var_pool)
);
return;
}
if def.defined_vars.is_empty() {
// This node doesn't define any values, so just insert the new instruction.
fmtln!(
fmt,
"pos.ins().{};",
def.apply.rust_builder(&def.defined_vars, var_pool)
);
return;
}
if let Some(src_def0) = var_pool.get(def.defined_vars[0]).src_def {
if def.defined_vars == def_pool.get(src_def0).defined_vars {
// The replacement instruction defines the exact same values as the source pattern.
// Unwrapping would have left the results intact. Replace the whole instruction.
fmtln!(
fmt,
"let {} = pos.func.dfg.replace(inst).{};",
defined_vars,
def.apply.rust_builder(&def.defined_vars, var_pool)
);
// We need to bump the cursor so following instructions are inserted *after* the
// replaced instruction.
fmt.line("if pos.current_inst() == Some(inst) {");
fmt.indent(|fmt| {
fmt.line("pos.next_inst();");
});
fmt.line("}");
return;
}
}
// Insert a new instruction.
let mut builder = format!("let {} = pos.ins()", defined_vars);
if def.defined_vars.len() == 1 && var_pool.get(def.defined_vars[0]).is_output() {
// Reuse the single source result value.
builder = format!(
"{}.with_result({})",
builder,
var_pool.get(def.defined_vars[0]).to_rust_code()
);
} else if def
.defined_vars
.iter()
.any(|&var_index| var_pool.get(var_index).is_output())
{
// There are more than one output values that can be reused.
let array = def
.defined_vars
.iter()
.map(|&var_index| {
let var = var_pool.get(var_index);
if var.is_output() {
format!("Some({})", var.name)
} else {
"None".into()
}
})
.collect::<Vec<_>>()
.join(", ");
builder = format!("{}.with_results([{}])", builder, array);
}
fmtln!(
fmt,
"{}.{};",
builder,
def.apply.rust_builder(&def.defined_vars, var_pool)
);
}
/// Emit code for `transform`, assuming that the opcode of transform's root instruction
/// has already been matched.
///
/// `inst: Inst` is the variable to be replaced. It is pointed to by `pos: Cursor`.
/// `dfg: DataFlowGraph` is available and mutable.
fn gen_transform<'a>(
transform: &'a Transform,
format_registry: &FormatRegistry,
type_sets: &mut UniqueTable<'a, TypeSet>,
fmt: &mut Formatter,
) {
// Unwrap the source instruction, create local variables for the input variables.
let replace_inst = unwrap_inst(&transform, format_registry, fmt);
// Emit any runtime checks; these will rebind `predicate` emitted by unwrap_inst().
for constraint in &transform.type_env.constraints {
emit_runtime_typecheck(constraint, type_sets, fmt);
}
// Guard the actual expansion by `predicate`.
fmt.line("if predicate {");
fmt.indent(|fmt| {
// If we're going to delete `inst`, we need to detach its results first so they can be
// reattached during pattern expansion.
if !replace_inst {
fmt.line("pos.func.dfg.clear_results(inst);");
}
// Emit the destination pattern.
for &def_index in &transform.dst {
emit_dst_inst(
transform.def_pool.get(def_index),
&transform.def_pool,
&transform.var_pool,
fmt,
);
}
// Delete the original instruction if we didn't have an opportunity to replace it.
if !replace_inst {
fmt.line("let removed = pos.remove_inst();");
fmt.line("debug_assert_eq!(removed, inst);");
}
fmt.line("return true;");
});
fmt.line("}");
}
fn gen_transform_group<'a>(
group: &'a TransformGroup,
format_registry: &FormatRegistry,
transform_groups: &TransformGroups,
type_sets: &mut UniqueTable<'a, TypeSet>,
fmt: &mut Formatter,
) {
fmt.doc_comment(group.doc);
fmt.line("#[allow(unused_variables,unused_assignments,non_snake_case)]");
// Function arguments.
fmtln!(fmt, "pub fn {}(", group.name);
fmt.indent(|fmt| {
fmt.line("inst: crate::ir::Inst,");
fmt.line("func: &mut crate::ir::Function,");
fmt.line("cfg: &mut crate::flowgraph::ControlFlowGraph,");
fmt.line("isa: &crate::isa::TargetIsa,");
});
fmtln!(fmt, ") -> bool {");
// Function body.
fmt.indent(|fmt| {
fmt.line("use crate::ir::InstBuilder;");
fmt.line("use crate::cursor::{Cursor, FuncCursor};");
fmt.line("let mut pos = FuncCursor::new(func).at_inst(inst);");
fmt.line("pos.use_srcloc(inst);");
// Group the transforms by opcode so we can generate a big switch.
// Preserve ordering.
let mut inst_to_transforms = HashMap::new();
for transform in &group.transforms {
let def_index = transform.src;
let inst = &transform.def_pool.get(def_index).apply.inst;
inst_to_transforms
.entry(inst.camel_name.clone())
.or_insert(Vec::new())
.push(transform);
}
let mut sorted_inst_names = Vec::from_iter(inst_to_transforms.keys());
sorted_inst_names.sort();
fmt.line("{");
fmt.indent(|fmt| {
fmt.line("match pos.func.dfg[inst].opcode() {");
fmt.indent(|fmt| {
for camel_name in sorted_inst_names {
fmtln!(fmt, "ir::Opcode::{} => {{", camel_name);
fmt.indent(|fmt| {
for transform in inst_to_transforms.get(camel_name).unwrap() {
gen_transform(transform, format_registry, type_sets, fmt);
}
});
fmtln!(fmt, "}");
fmt.empty_line();
}
// Emit the custom transforms. The Rust compiler will complain about any overlap with
// the normal transforms.
for (inst_camel_name, func_name) in &group.custom_legalizes {
fmtln!(fmt, "ir::Opcode::{} => {{", inst_camel_name);
fmt.indent(|fmt| {
fmtln!(fmt, "{}(inst, pos.func, cfg, isa);", func_name);
fmt.line("return true;");
});
fmtln!(fmt, "}");
fmt.empty_line();
}
// We'll assume there are uncovered opcodes.
fmt.line("_ => {},");
});
fmt.line("}");
});
fmt.line("}");
// If we fall through, nothing was expanded; call the chain if any.
match &group.chain_with {
Some(group_id) => fmtln!(
fmt,
"{}(inst, pos.func, cfg, isa)",
transform_groups.get(*group_id).rust_name()
),
None => fmt.line("false"),
};
});
fmtln!(fmt, "}");
fmt.empty_line();
}
/// Generate legalization functions for `isa` and add any shared `TransformGroup`s
/// encountered to `shared_groups`.
///
/// Generate `TYPE_SETS` and `LEGALIZE_ACTIONS` tables.
fn gen_isa(
isa: &TargetIsa,
format_registry: &FormatRegistry,
transform_groups: &TransformGroups,
shared_group_names: &mut HashSet<&'static str>,
fmt: &mut Formatter,
) {
let mut type_sets = UniqueTable::new();
for group_index in isa.transitive_transform_groups(transform_groups) {
let group = transform_groups.get(group_index);
match group.isa_name {
Some(isa_name) => {
assert!(
isa_name == isa.name,
"ISA-specific legalizations must be used by the same ISA"
);
gen_transform_group(
group,
format_registry,
transform_groups,
&mut type_sets,
fmt,
);
}
None => {
shared_group_names.insert(group.name);
}
}
}
gen_typesets_table(&type_sets, fmt);
let direct_groups = isa.direct_transform_groups();
fmtln!(
fmt,
"pub static LEGALIZE_ACTIONS: [isa::Legalize; {}] = [",
direct_groups.len()
);
fmt.indent(|fmt| {
for group_index in direct_groups {
fmtln!(fmt, "{},", transform_groups.get(group_index).rust_name());
}
});
fmtln!(fmt, "];");
}
/// Generate the legalizer files.
pub fn generate(
isas: &Vec<TargetIsa>,
format_registry: &FormatRegistry,
transform_groups: &TransformGroups,
filename_prefix: &str,
out_dir: &str,
) -> Result<(), error::Error> {
let mut shared_group_names = HashSet::new();
for isa in isas {
let mut fmt = Formatter::new();
gen_isa(
isa,
format_registry,
transform_groups,
&mut shared_group_names,
&mut fmt,
);
fmt.update_file(format!("{}-{}.rs", filename_prefix, isa.name), out_dir)?;
}
// Generate shared legalize groups.
let mut fmt = Formatter::new();
let mut type_sets = UniqueTable::new();
let mut sorted_shared_group_names = Vec::from_iter(shared_group_names);
sorted_shared_group_names.sort();
for group_name in &sorted_shared_group_names {
let group = transform_groups.by_name(group_name);
gen_transform_group(
group,
format_registry,
transform_groups,
&mut type_sets,
&mut fmt,
);
}
gen_typesets_table(&type_sets, &mut fmt);
fmt.update_file(format!("{}r.rs", filename_prefix), out_dir)?;
Ok(())
}

View File

@ -1,9 +1,7 @@
use crate::cdsl::cpu_modes::CpuMode;
use crate::cdsl::inst::InstructionGroup;
use crate::cdsl::isa::TargetIsa;
use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder};
use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder};
use crate::shared::Definitions as SharedDefinitions;
fn define_settings(_shared: &SettingGroup) -> SettingGroup {
@ -54,16 +52,5 @@ pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
let inst_group = InstructionGroup::new("arm32", "arm32 specific instruction set");
// CPU modes for 32-bit ARM and Thumb2.
let mut a32 = CpuMode::new("A32");
let mut t32 = CpuMode::new("T32");
// TODO refine these.
let narrow = shared_defs.transform_groups.by_name("narrow");
a32.legalize_default(narrow);
t32.legalize_default(narrow);
let cpu_modes = vec![a32, t32];
TargetIsa::new("arm32", inst_group, settings, regs, cpu_modes)
TargetIsa::new("arm32", inst_group, settings, regs)
}

View File

@ -1,9 +1,7 @@
use crate::cdsl::cpu_modes::CpuMode;
use crate::cdsl::inst::InstructionGroup;
use crate::cdsl::isa::TargetIsa;
use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder};
use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder};
use crate::shared::Definitions as SharedDefinitions;
fn define_settings(_shared: &SettingGroup) -> SettingGroup {
@ -50,13 +48,5 @@ pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
let inst_group = InstructionGroup::new("arm64", "arm64 specific instruction set");
let mut a64 = CpuMode::new("A64");
// TODO refine these.
let narrow = shared_defs.transform_groups.by_name("narrow");
a64.legalize_default(narrow);
let cpu_modes = vec![a64];
TargetIsa::new("arm64", inst_group, settings, regs, cpu_modes)
TargetIsa::new("arm64", inst_group, settings, regs)
}

View File

@ -1,11 +1,7 @@
use crate::cdsl::cpu_modes::CpuMode;
use crate::cdsl::inst::InstructionGroup;
use crate::cdsl::isa::TargetIsa;
use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder};
use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};
use crate::shared::types::Float::{F32, F64};
use crate::shared::types::Int::{I32, I64};
use crate::shared::Definitions as SharedDefinitions;
fn define_settings(shared: &SettingGroup) -> SettingGroup {
@ -88,26 +84,5 @@ pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
let inst_group = InstructionGroup::new("riscv", "riscv specific instruction set");
// CPU modes for 32-bit and 64-bit operation.
let mut rv_32 = CpuMode::new("RV32");
let mut rv_64 = CpuMode::new("RV64");
let expand = shared_defs.transform_groups.by_name("expand");
let narrow = shared_defs.transform_groups.by_name("narrow");
rv_32.legalize_monomorphic(expand);
rv_32.legalize_default(narrow);
rv_32.legalize_type(I32, expand);
rv_32.legalize_type(F32, expand);
rv_32.legalize_type(F64, expand);
rv_64.legalize_monomorphic(expand);
rv_64.legalize_default(narrow);
rv_64.legalize_type(I32, expand);
rv_64.legalize_type(I64, expand);
rv_64.legalize_type(F32, expand);
rv_64.legalize_type(F64, expand);
let cpu_modes = vec![rv_32, rv_64];
TargetIsa::new("riscv", inst_group, settings, regs, cpu_modes)
TargetIsa::new("riscv", inst_group, settings, regs)
}

View File

@ -1,293 +0,0 @@
use crate::cdsl::ast::{bind, var, ExprBuilder, Literal};
use crate::cdsl::inst::InstructionGroup;
use crate::cdsl::xform::TransformGroupBuilder;
use crate::shared::types::Int::{I32, I64};
use crate::shared::Definitions as SharedDefinitions;
pub fn define(shared: &mut SharedDefinitions, x86_instructions: &InstructionGroup) {
let mut group = TransformGroupBuilder::new(
"x86_expand",
r#"
Legalize instructions by expansion.
Use x86-specific instructions if needed."#,
)
.isa("x86")
.chain_with(shared.transform_groups.by_name("expand_flags").id);
// List of instructions.
let insts = &shared.instructions;
let band = insts.by_name("band");
let bor = insts.by_name("bor");
let clz = insts.by_name("clz");
let ctz = insts.by_name("ctz");
let fcmp = insts.by_name("fcmp");
let fcvt_from_uint = insts.by_name("fcvt_from_uint");
let fcvt_to_sint = insts.by_name("fcvt_to_sint");
let fcvt_to_uint = insts.by_name("fcvt_to_uint");
let fcvt_to_sint_sat = insts.by_name("fcvt_to_sint_sat");
let fcvt_to_uint_sat = insts.by_name("fcvt_to_uint_sat");
let fmax = insts.by_name("fmax");
let fmin = insts.by_name("fmin");
let iadd = insts.by_name("iadd");
let iconst = insts.by_name("iconst");
let imul = insts.by_name("imul");
let isub = insts.by_name("isub");
let popcnt = insts.by_name("popcnt");
let sdiv = insts.by_name("sdiv");
let selectif = insts.by_name("selectif");
let smulhi = insts.by_name("smulhi");
let srem = insts.by_name("srem");
let udiv = insts.by_name("udiv");
let umulhi = insts.by_name("umulhi");
let ushr_imm = insts.by_name("ushr_imm");
let urem = insts.by_name("urem");
let x86_bsf = x86_instructions.by_name("x86_bsf");
let x86_bsr = x86_instructions.by_name("x86_bsr");
let x86_umulx = x86_instructions.by_name("x86_umulx");
let x86_smulx = x86_instructions.by_name("x86_smulx");
// List of immediates.
let floatcc = shared.operand_kinds.by_name("floatcc");
let imm64 = shared.operand_kinds.by_name("imm64");
let intcc = shared.operand_kinds.by_name("intcc");
// Division and remainder.
//
// The srem expansion requires custom code because srem INT_MIN, -1 is not
// allowed to trap. The other ops need to check avoid_div_traps.
group.custom_legalize(sdiv, "expand_sdivrem");
group.custom_legalize(srem, "expand_sdivrem");
group.custom_legalize(udiv, "expand_udivrem");
group.custom_legalize(urem, "expand_udivrem");
// Double length (widening) multiplication.
let a = var("a");
let x = var("x");
let y = var("y");
let a1 = var("a1");
let a2 = var("a2");
let res_lo = var("res_lo");
let res_hi = var("res_hi");
group.legalize(
def!(res_hi = umulhi(x, y)),
vec![def!((res_lo, res_hi) = x86_umulx(x, y))],
);
group.legalize(
def!(res_hi = smulhi(x, y)),
vec![def!((res_lo, res_hi) = x86_smulx(x, y))],
);
// Floating point condition codes.
//
// The 8 condition codes in `supported_floatccs` are directly supported by a
// `ucomiss` or `ucomisd` instruction. The remaining codes need legalization
// patterns.
let floatcc_eq = Literal::enumerator_for(floatcc, "eq");
let floatcc_ord = Literal::enumerator_for(floatcc, "ord");
let floatcc_ueq = Literal::enumerator_for(floatcc, "ueq");
let floatcc_ne = Literal::enumerator_for(floatcc, "ne");
let floatcc_uno = Literal::enumerator_for(floatcc, "uno");
let floatcc_one = Literal::enumerator_for(floatcc, "one");
// Equality needs an explicit `ord` test which checks the parity bit.
group.legalize(
def!(a = fcmp(floatcc_eq, x, y)),
vec![
def!(a1 = fcmp(floatcc_ord, x, y)),
def!(a2 = fcmp(floatcc_ueq, x, y)),
def!(a = band(a1, a2)),
],
);
group.legalize(
def!(a = fcmp(floatcc_ne, x, y)),
vec![
def!(a1 = fcmp(floatcc_uno, x, y)),
def!(a2 = fcmp(floatcc_one, x, y)),
def!(a = bor(a1, a2)),
],
);
let floatcc_lt = &Literal::enumerator_for(floatcc, "lt");
let floatcc_gt = &Literal::enumerator_for(floatcc, "gt");
let floatcc_le = &Literal::enumerator_for(floatcc, "le");
let floatcc_ge = &Literal::enumerator_for(floatcc, "ge");
let floatcc_ugt = &Literal::enumerator_for(floatcc, "ugt");
let floatcc_ult = &Literal::enumerator_for(floatcc, "ult");
let floatcc_uge = &Literal::enumerator_for(floatcc, "uge");
let floatcc_ule = &Literal::enumerator_for(floatcc, "ule");
// Inequalities that need to be reversed.
for &(cc, rev_cc) in &[
(floatcc_lt, floatcc_gt),
(floatcc_le, floatcc_ge),
(floatcc_ugt, floatcc_ult),
(floatcc_uge, floatcc_ule),
] {
group.legalize(def!(a = fcmp(cc, x, y)), vec![def!(a = fcmp(rev_cc, y, x))]);
}
// We need to modify the CFG for min/max legalization.
group.custom_legalize(fmin, "expand_minmax");
group.custom_legalize(fmax, "expand_minmax");
// Conversions from unsigned need special handling.
group.custom_legalize(fcvt_from_uint, "expand_fcvt_from_uint");
// Conversions from float to int can trap and modify the control flow graph.
group.custom_legalize(fcvt_to_sint, "expand_fcvt_to_sint");
group.custom_legalize(fcvt_to_uint, "expand_fcvt_to_uint");
group.custom_legalize(fcvt_to_sint_sat, "expand_fcvt_to_sint_sat");
group.custom_legalize(fcvt_to_uint_sat, "expand_fcvt_to_uint_sat");
// Count leading and trailing zeroes, for baseline x86_64
let c_minus_one = var("c_minus_one");
let c_thirty_one = var("c_thirty_one");
let c_thirty_two = var("c_thirty_two");
let c_sixty_three = var("c_sixty_three");
let c_sixty_four = var("c_sixty_four");
let index1 = var("index1");
let r2flags = var("r2flags");
let index2 = var("index2");
let intcc_eq = Literal::enumerator_for(intcc, "eq");
let imm64_minus_one = Literal::constant(imm64, -1);
let imm64_63 = Literal::constant(imm64, 63);
group.legalize(
def!(a = clz.I64(x)),
vec![
def!(c_minus_one = iconst(imm64_minus_one)),
def!(c_sixty_three = iconst(imm64_63)),
def!((index1, r2flags) = x86_bsr(x)),
def!(index2 = selectif(intcc_eq, r2flags, c_minus_one, index1)),
def!(a = isub(c_sixty_three, index2)),
],
);
let imm64_31 = Literal::constant(imm64, 31);
group.legalize(
def!(a = clz.I32(x)),
vec![
def!(c_minus_one = iconst(imm64_minus_one)),
def!(c_thirty_one = iconst(imm64_31)),
def!((index1, r2flags) = x86_bsr(x)),
def!(index2 = selectif(intcc_eq, r2flags, c_minus_one, index1)),
def!(a = isub(c_thirty_one, index2)),
],
);
let imm64_64 = Literal::constant(imm64, 64);
group.legalize(
def!(a = ctz.I64(x)),
vec![
def!(c_sixty_four = iconst(imm64_64)),
def!((index1, r2flags) = x86_bsf(x)),
def!(a = selectif(intcc_eq, r2flags, c_sixty_four, index1)),
],
);
let imm64_32 = Literal::constant(imm64, 32);
group.legalize(
def!(a = ctz.I32(x)),
vec![
def!(c_thirty_two = iconst(imm64_32)),
def!((index1, r2flags) = x86_bsf(x)),
def!(a = selectif(intcc_eq, r2flags, c_thirty_two, index1)),
],
);
// Population count for baseline x86_64
let qv1 = var("qv1");
let qv3 = var("qv3");
let qv4 = var("qv4");
let qv5 = var("qv5");
let qv6 = var("qv6");
let qv7 = var("qv7");
let qv8 = var("qv8");
let qv9 = var("qv9");
let qv10 = var("qv10");
let qv11 = var("qv11");
let qv12 = var("qv12");
let qv13 = var("qv13");
let qv14 = var("qv14");
let qv15 = var("qv15");
let qv16 = var("qv16");
let qc77 = var("qc77");
#[allow(non_snake_case)]
let qc0F = var("qc0F");
let qc01 = var("qc01");
let imm64_1 = Literal::constant(imm64, 1);
let imm64_4 = Literal::constant(imm64, 4);
group.legalize(
def!(qv16 = popcnt.I64(qv1)),
vec![
def!(qv3 = ushr_imm(qv1, imm64_1)),
def!(qc77 = iconst(Literal::constant(imm64, 0x7777777777777777))),
def!(qv4 = band(qv3, qc77)),
def!(qv5 = isub(qv1, qv4)),
def!(qv6 = ushr_imm(qv4, imm64_1)),
def!(qv7 = band(qv6, qc77)),
def!(qv8 = isub(qv5, qv7)),
def!(qv9 = ushr_imm(qv7, imm64_1)),
def!(qv10 = band(qv9, qc77)),
def!(qv11 = isub(qv8, qv10)),
def!(qv12 = ushr_imm(qv11, imm64_4)),
def!(qv13 = iadd(qv11, qv12)),
def!(qc0F = iconst(Literal::constant(imm64, 0x0F0F0F0F0F0F0F0F))),
def!(qv14 = band(qv13, qc0F)),
def!(qc01 = iconst(Literal::constant(imm64, 0x0101010101010101))),
def!(qv15 = imul(qv14, qc01)),
def!(qv16 = ushr_imm(qv15, Literal::constant(imm64, 56))),
],
);
let lv1 = var("lv1");
let lv3 = var("lv3");
let lv4 = var("lv4");
let lv5 = var("lv5");
let lv6 = var("lv6");
let lv7 = var("lv7");
let lv8 = var("lv8");
let lv9 = var("lv9");
let lv10 = var("lv10");
let lv11 = var("lv11");
let lv12 = var("lv12");
let lv13 = var("lv13");
let lv14 = var("lv14");
let lv15 = var("lv15");
let lv16 = var("lv16");
let lc77 = var("lc77");
#[allow(non_snake_case)]
let lc0F = var("lc0F");
let lc01 = var("lc01");
group.legalize(
def!(lv16 = popcnt.I32(lv1)),
vec![
def!(lv3 = ushr_imm(lv1, imm64_1)),
def!(lc77 = iconst(Literal::constant(imm64, 0x77777777))),
def!(lv4 = band(lv3, lc77)),
def!(lv5 = isub(lv1, lv4)),
def!(lv6 = ushr_imm(lv4, imm64_1)),
def!(lv7 = band(lv6, lc77)),
def!(lv8 = isub(lv5, lv7)),
def!(lv9 = ushr_imm(lv7, imm64_1)),
def!(lv10 = band(lv9, lc77)),
def!(lv11 = isub(lv8, lv10)),
def!(lv12 = ushr_imm(lv11, imm64_4)),
def!(lv13 = iadd(lv11, lv12)),
def!(lc0F = iconst(Literal::constant(imm64, 0x0F0F0F0F))),
def!(lv14 = band(lv13, lc0F)),
def!(lc01 = iconst(Literal::constant(imm64, 0x01010101))),
def!(lv15 = imul(lv14, lc01)),
def!(lv16 = ushr_imm(lv15, Literal::constant(imm64, 24))),
],
);
group.finish_and_add_to(&mut shared.transform_groups);
}

View File

@ -1,16 +1,11 @@
use crate::cdsl::cpu_modes::CpuMode;
mod instructions;
use crate::cdsl::isa::TargetIsa;
use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder};
use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};
use crate::shared::types::Bool::B1;
use crate::shared::types::Float::{F32, F64};
use crate::shared::types::Int::{I16, I32, I64, I8};
use crate::shared::Definitions as SharedDefinitions;
mod instructions;
mod legalize;
fn define_settings(_shared: &SettingGroup) -> SettingGroup {
let mut settings = SettingGroupBuilder::new("x86");
@ -123,37 +118,6 @@ pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
let regs = define_registers();
let inst_group = instructions::define(&shared_defs.format_registry);
legalize::define(shared_defs, &inst_group);
// CPU modes for 32-bit and 64-bit operations.
let mut x86_64 = CpuMode::new("I64");
let mut x86_32 = CpuMode::new("I32");
let expand_flags = shared_defs.transform_groups.by_name("expand_flags");
let narrow = shared_defs.transform_groups.by_name("narrow");
let widen = shared_defs.transform_groups.by_name("widen");
let x86_expand = shared_defs.transform_groups.by_name("x86_expand");
x86_32.legalize_monomorphic(expand_flags);
x86_32.legalize_default(narrow);
x86_32.legalize_type(B1, expand_flags);
x86_32.legalize_type(I8, widen);
x86_32.legalize_type(I16, widen);
x86_32.legalize_type(I32, x86_expand);
x86_32.legalize_type(F32, x86_expand);
x86_32.legalize_type(F64, x86_expand);
x86_64.legalize_monomorphic(expand_flags);
x86_64.legalize_default(narrow);
x86_64.legalize_type(B1, expand_flags);
x86_64.legalize_type(I8, widen);
x86_64.legalize_type(I16, widen);
x86_64.legalize_type(I32, x86_expand);
x86_64.legalize_type(I64, x86_expand);
x86_64.legalize_type(F32, x86_expand);
x86_64.legalize_type(F64, x86_expand);
let cpu_modes = vec![x86_64, x86_32];
TargetIsa::new("x86", inst_group, settings, regs, cpu_modes)
TargetIsa::new("x86", inst_group, settings, regs)
}

View File

@ -6,7 +6,6 @@ pub mod error;
pub mod isa;
mod gen_inst;
mod gen_legalizer;
mod gen_registers;
mod gen_settings;
mod gen_types;
@ -46,14 +45,6 @@ pub fn generate(isas: &Vec<isa::Isa>, out_dir: &str) -> Result<(), error::Error>
&out_dir,
)?;
gen_legalizer::generate(
&isas,
&shared_defs.format_registry,
&shared_defs.transform_groups,
"new_legalize",
&out_dir,
)?;
for isa in isas {
gen_registers::generate(&isa, &format!("registers-{}.rs", isa.name), &out_dir)?;
gen_settings::generate(

View File

@ -71,10 +71,10 @@ pub fn define() -> Vec<OperandKind> {
let mut intcc_values = HashMap::new();
intcc_values.insert("eq", "Equal");
intcc_values.insert("ne", "NotEqual");
intcc_values.insert("sge", "SignedGreaterThanOrEqual");
intcc_values.insert("sgt", "SignedGreaterThan");
intcc_values.insert("sle", "SignedLessThanOrEqual");
intcc_values.insert("slt", "SignedLessThan");
intcc_values.insert("sge", "UnsignedGreaterThanOrEqual");
intcc_values.insert("sgt", "UnsignedGreaterThan");
intcc_values.insert("sle", "UnsignedLessThanOrEqual");
intcc_values.insert("slt", "UnsignedLessThan");
intcc_values.insert("uge", "UnsignedGreaterThanOrEqual");
intcc_values.insert("ugt", "UnsignedGreaterThan");
intcc_values.insert("ule", "UnsignedLessThanOrEqual");

View File

@ -1,785 +0,0 @@
use crate::cdsl::ast::{bind, var, ExprBuilder, Literal};
use crate::cdsl::inst::{Instruction, InstructionGroup};
use crate::cdsl::xform::{TransformGroupBuilder, TransformGroups};
use crate::shared::OperandKinds;
use crate::shared::types::Float::{F32, F64};
use crate::shared::types::Int::{I16, I32, I64, I8};
pub fn define(insts: &InstructionGroup, immediates: &OperandKinds) -> TransformGroups {
let mut narrow = TransformGroupBuilder::new(
"narrow",
r#"
Legalize instructions by narrowing.
The transformations in the 'narrow' group work by expressing
instructions in terms of smaller types. Operations on vector types are
expressed in terms of vector types with fewer lanes, and integer
operations are expressed in terms of smaller integer types.
"#,
);
let mut widen = TransformGroupBuilder::new(
"widen",
r#"
Legalize instructions by widening.
The transformations in the 'widen' group work by expressing
instructions in terms of larger types.
"#,
);
let mut expand = TransformGroupBuilder::new(
"expand",
r#"
Legalize instructions by expansion.
Rewrite instructions in terms of other instructions, generally
operating on the same types as the original instructions.
"#,
);
// List of instructions.
let band = insts.by_name("band");
let band_imm = insts.by_name("band_imm");
let band_not = insts.by_name("band_not");
let bint = insts.by_name("bint");
let bitrev = insts.by_name("bitrev");
let bnot = insts.by_name("bnot");
let bor = insts.by_name("bor");
let bor_imm = insts.by_name("bor_imm");
let bor_not = insts.by_name("bor_not");
let br_icmp = insts.by_name("br_icmp");
let br_table = insts.by_name("br_table");
let bxor = insts.by_name("bxor");
let bxor_imm = insts.by_name("bxor_imm");
let bxor_not = insts.by_name("bxor_not");
let cls = insts.by_name("cls");
let clz = insts.by_name("clz");
let ctz = insts.by_name("ctz");
let fabs = insts.by_name("fabs");
let f32const = insts.by_name("f32const");
let f64const = insts.by_name("f64const");
let fcopysign = insts.by_name("fcopysign");
let fneg = insts.by_name("fneg");
let iadd = insts.by_name("iadd");
let iadd_carry = insts.by_name("iadd_carry");
let iadd_cin = insts.by_name("iadd_cin");
let iadd_cout = insts.by_name("iadd_cout");
let iadd_imm = insts.by_name("iadd_imm");
let icmp = insts.by_name("icmp");
let icmp_imm = insts.by_name("icmp_imm");
let iconcat = insts.by_name("iconcat");
let iconst = insts.by_name("iconst");
let ifcmp = insts.by_name("ifcmp");
let ifcmp_imm = insts.by_name("ifcmp_imm");
let imul = insts.by_name("imul");
let imul_imm = insts.by_name("imul_imm");
let ireduce = insts.by_name("ireduce");
let irsub_imm = insts.by_name("irsub_imm");
let ishl = insts.by_name("ishl");
let ishl_imm = insts.by_name("ishl_imm");
let isplit = insts.by_name("isplit");
let istore8 = insts.by_name("istore8");
let istore16 = insts.by_name("istore16");
let isub = insts.by_name("isub");
let isub_bin = insts.by_name("isub_bin");
let isub_borrow = insts.by_name("isub_borrow");
let isub_bout = insts.by_name("isub_bout");
let load = insts.by_name("load");
let popcnt = insts.by_name("popcnt");
let rotl = insts.by_name("rotl");
let rotl_imm = insts.by_name("rotl_imm");
let rotr = insts.by_name("rotr");
let rotr_imm = insts.by_name("rotr_imm");
let sdiv = insts.by_name("sdiv");
let sdiv_imm = insts.by_name("sdiv_imm");
let select = insts.by_name("select");
let sextend = insts.by_name("sextend");
let sshr = insts.by_name("sshr");
let sshr_imm = insts.by_name("sshr_imm");
let srem = insts.by_name("srem");
let srem_imm = insts.by_name("srem_imm");
let store = insts.by_name("store");
let udiv = insts.by_name("udiv");
let udiv_imm = insts.by_name("udiv_imm");
let uextend = insts.by_name("uextend");
let uload8 = insts.by_name("uload8");
let uload16 = insts.by_name("uload16");
let ushr = insts.by_name("ushr");
let ushr_imm = insts.by_name("ushr_imm");
let urem = insts.by_name("urem");
let urem_imm = insts.by_name("urem_imm");
let trapif = insts.by_name("trapif");
let trapnz = insts.by_name("trapnz");
let trapz = insts.by_name("trapz");
// Custom expansions for memory objects.
expand.custom_legalize(insts.by_name("global_value"), "expand_global_value");
expand.custom_legalize(insts.by_name("heap_addr"), "expand_heap_addr");
expand.custom_legalize(insts.by_name("table_addr"), "expand_table_addr");
// Custom expansions for calls.
expand.custom_legalize(insts.by_name("call"), "expand_call");
// Custom expansions that need to change the CFG.
// TODO: Add sufficient XForm syntax that we don't need to hand-code these.
expand.custom_legalize(trapz, "expand_cond_trap");
expand.custom_legalize(trapnz, "expand_cond_trap");
expand.custom_legalize(br_table, "expand_br_table");
expand.custom_legalize(select, "expand_select");
// Custom expansions for floating point constants.
// These expansions require bit-casting or creating constant pool entries.
expand.custom_legalize(f32const, "expand_fconst");
expand.custom_legalize(f64const, "expand_fconst");
// Custom expansions for stack memory accesses.
expand.custom_legalize(insts.by_name("stack_load"), "expand_stack_load");
expand.custom_legalize(insts.by_name("stack_store"), "expand_stack_store");
// List of immediates.
let imm64 = immediates.by_name("imm64");
let ieee32 = immediates.by_name("ieee32");
let ieee64 = immediates.by_name("ieee64");
let intcc = immediates.by_name("intcc");
// List of variables to reuse in patterns.
let x = var("x");
let y = var("y");
let z = var("z");
let a = var("a");
let a1 = var("a1");
let a2 = var("a2");
let a3 = var("a3");
let a4 = var("a4");
let b = var("b");
let b1 = var("b1");
let b2 = var("b2");
let b3 = var("b3");
let b4 = var("b4");
let b_in = var("b_in");
let b_int = var("b_int");
let c = var("c");
let c1 = var("c1");
let c2 = var("c2");
let c3 = var("c3");
let c4 = var("c4");
let c_in = var("c_in");
let c_int = var("c_int");
let d = var("d");
let d1 = var("d1");
let d2 = var("d2");
let d3 = var("d3");
let d4 = var("d4");
let e = var("e");
let e1 = var("e1");
let e2 = var("e2");
let e3 = var("e3");
let e4 = var("e4");
let f = var("f");
let f1 = var("f1");
let f2 = var("f2");
let xl = var("xl");
let xh = var("xh");
let yl = var("yl");
let yh = var("yh");
let al = var("al");
let ah = var("ah");
let cc = var("cc");
let ptr = var("ptr");
let flags = var("flags");
let offset = var("off");
narrow.legalize(
def!(a = iadd(x, y)),
vec![
def!((xl, xh) = isplit(x)),
def!((yl, yh) = isplit(y)),
def!((al, c) = iadd_cout(xl, yl)),
def!(ah = iadd_cin(xh, yh, c)),
def!(a = iconcat(al, ah)),
],
);
narrow.legalize(
def!(a = isub(x, y)),
vec![
def!((xl, xh) = isplit(x)),
def!((yl, yh) = isplit(y)),
def!((al, b) = isub_bout(xl, yl)),
def!(ah = isub_bin(xh, yh, b)),
def!(a = iconcat(al, ah)),
],
);
for &bin_op in &[band, bor, bxor] {
narrow.legalize(
def!(a = bin_op(x, y)),
vec![
def!((xl, xh) = isplit(x)),
def!((yl, yh) = isplit(y)),
def!(al = bin_op(xl, yl)),
def!(ah = bin_op(xh, yh)),
def!(a = iconcat(al, ah)),
],
);
}
narrow.legalize(
def!(a = select(c, x, y)),
vec![
def!((xl, xh) = isplit(x)),
def!((yl, yh) = isplit(y)),
def!(al = select(c, xl, yl)),
def!(ah = select(c, xh, yh)),
def!(a = iconcat(al, ah)),
],
);
// Widen instructions with one input operand.
for &op in &[bnot, popcnt] {
for &int_ty in &[I8, I16] {
widen.legalize(
def!(a = op.int_ty(b)),
vec![
def!(x = uextend.I32(b)),
def!(z = op.I32(x)),
def!(a = ireduce.int_ty(z)),
],
);
}
}
// Widen instructions with two input operands.
let mut widen_two_arg = |signed: bool, op: &Instruction| {
for &int_ty in &[I8, I16] {
let sign_ext_op = if signed { sextend } else { uextend };
widen.legalize(
def!(a = op.int_ty(b, c)),
vec![
def!(x = sign_ext_op.I32(b)),
def!(y = sign_ext_op.I32(c)),
def!(z = op.I32(x, y)),
def!(a = ireduce.int_ty(z)),
],
);
}
};
for bin_op in &[
iadd, isub, imul, udiv, urem, band, bor, bxor, band_not, bor_not, bxor_not,
] {
widen_two_arg(false, bin_op);
}
for bin_op in &[sdiv, srem] {
widen_two_arg(true, bin_op);
}
// Widen instructions using immediate operands.
let mut widen_imm = |signed: bool, op: &Instruction| {
for &int_ty in &[I8, I16] {
let sign_ext_op = if signed { sextend } else { uextend };
widen.legalize(
def!(a = op.int_ty(b, c)),
vec![
def!(x = sign_ext_op.I32(b)),
def!(z = op.I32(x, c)),
def!(a = ireduce.int_ty(z)),
],
);
}
};
for bin_op in &[
iadd_imm, imul_imm, udiv_imm, urem_imm, band_imm, bor_imm, bxor_imm, irsub_imm,
] {
widen_imm(false, bin_op);
}
for bin_op in &[sdiv_imm, srem_imm] {
widen_imm(true, bin_op);
}
for &(int_ty, num) in &[(I8, 24), (I16, 16)] {
let imm = Literal::constant(imm64, -num);
widen.legalize(
def!(a = clz.int_ty(b)),
vec![
def!(c = uextend.I32(b)),
def!(d = clz.I32(c)),
def!(e = iadd_imm(d, imm)),
def!(a = ireduce.int_ty(e)),
],
);
widen.legalize(
def!(a = cls.int_ty(b)),
vec![
def!(c = sextend.I32(b)),
def!(d = cls.I32(c)),
def!(e = iadd_imm(d, imm)),
def!(a = ireduce.int_ty(e)),
],
);
}
for &(int_ty, num) in &[(I8, 1 << 8), (I16, 1 << 16)] {
let num = Literal::constant(imm64, num);
widen.legalize(
def!(a = ctz.int_ty(b)),
vec![
def!(c = uextend.I32(b)),
// When `b` is zero, returns the size of x in bits.
def!(d = bor_imm(c, num)),
def!(e = ctz.I32(d)),
def!(a = ireduce.int_ty(e)),
],
);
}
// iconst
for &int_ty in &[I8, I16] {
widen.legalize(
def!(a = iconst.int_ty(b)),
vec![def!(c = iconst.I32(b)), def!(a = ireduce.int_ty(c))],
);
}
for &extend_op in &[uextend, sextend] {
// The sign extension operators have two typevars: the result has one and controls the
// instruction, then the input has one.
let bound = bind(bind(extend_op, I16), I8);
widen.legalize(
def!(a = bound(b)),
vec![def!(c = extend_op.I32(b)), def!(a = ireduce(c))],
);
}
widen.legalize(
def!(store.I8(flags, a, ptr, offset)),
vec![
def!(b = uextend.I32(a)),
def!(istore8(flags, b, ptr, offset)),
],
);
widen.legalize(
def!(store.I16(flags, a, ptr, offset)),
vec![
def!(b = uextend.I32(a)),
def!(istore16(flags, b, ptr, offset)),
],
);
widen.legalize(
def!(a = load.I8(flags, ptr, offset)),
vec![
def!(b = uload8.I32(flags, ptr, offset)),
def!(a = ireduce(b)),
],
);
widen.legalize(
def!(a = load.I16(flags, ptr, offset)),
vec![
def!(b = uload16.I32(flags, ptr, offset)),
def!(a = ireduce(b)),
],
);
for &int_ty in &[I8, I16] {
widen.legalize(
def!(br_table.int_ty(x, y, z)),
vec![def!(b = uextend.I32(x)), def!(br_table(b, y, z))],
);
}
for &int_ty in &[I8, I16] {
widen.legalize(
def!(a = bint.int_ty(b)),
vec![def!(x = bint.I32(b)), def!(a = ireduce.int_ty(x))],
);
}
for &int_ty in &[I8, I16] {
for &op in &[ishl, ishl_imm, ushr, ushr_imm] {
widen.legalize(
def!(a = op.int_ty(b, c)),
vec![
def!(x = uextend.I32(b)),
def!(z = op.I32(x, c)),
def!(a = ireduce.int_ty(z)),
],
);
}
for &op in &[sshr, sshr_imm] {
widen.legalize(
def!(a = op.int_ty(b, c)),
vec![
def!(x = sextend.I32(b)),
def!(z = op.I32(x, c)),
def!(a = ireduce.int_ty(z)),
],
);
}
for cc in &["eq", "ne", "ugt", "ult", "uge", "ule"] {
let w_cc = Literal::enumerator_for(intcc, cc);
widen.legalize(
def!(a = icmp_imm.int_ty(w_cc, b, c)),
vec![def!(x = uextend.I32(b)), def!(a = icmp_imm(w_cc, x, c))],
);
widen.legalize(
def!(a = icmp.int_ty(w_cc, b, c)),
vec![
def!(x = uextend.I32(b)),
def!(y = uextend.I32(c)),
def!(a = icmp.I32(w_cc, x, y)),
],
);
}
for cc in &["sgt", "slt", "sge", "sle"] {
let w_cc = Literal::enumerator_for(intcc, cc);
widen.legalize(
def!(a = icmp_imm.int_ty(w_cc, b, c)),
vec![def!(x = sextend.I32(b)), def!(a = icmp_imm(w_cc, x, c))],
);
widen.legalize(
def!(a = icmp.int_ty(w_cc, b, c)),
vec![
def!(x = sextend.I32(b)),
def!(y = sextend.I32(c)),
def!(a = icmp(w_cc, x, y)),
],
);
}
}
// Expand integer operations with carry for RISC architectures that don't have
// the flags.
let intcc_ult = Literal::enumerator_for(intcc, "ult");
expand.legalize(
def!((a, c) = iadd_cout(x, y)),
vec![def!(a = iadd(x, y)), def!(c = icmp(intcc_ult, a, x))],
);
let intcc_ugt = Literal::enumerator_for(intcc, "ugt");
expand.legalize(
def!((a, b) = isub_bout(x, y)),
vec![def!(a = isub(x, y)), def!(b = icmp(intcc_ugt, a, x))],
);
expand.legalize(
def!(a = iadd_cin(x, y, c)),
vec![
def!(a1 = iadd(x, y)),
def!(c_int = bint(c)),
def!(a = iadd(a1, c_int)),
],
);
expand.legalize(
def!(a = isub_bin(x, y, b)),
vec![
def!(a1 = isub(x, y)),
def!(b_int = bint(b)),
def!(a = isub(a1, b_int)),
],
);
expand.legalize(
def!((a, c) = iadd_carry(x, y, c_in)),
vec![
def!((a1, c1) = iadd_cout(x, y)),
def!(c_int = bint(c_in)),
def!((a, c2) = iadd_cout(a1, c_int)),
def!(c = bor(c1, c2)),
],
);
expand.legalize(
def!((a, b) = isub_borrow(x, y, b_in)),
vec![
def!((a1, b1) = isub_bout(x, y)),
def!(b_int = bint(b_in)),
def!((a, b2) = isub_bout(a1, b_int)),
def!(b = bor(b1, b2)),
],
);
// Expansions for immediate operands that are out of range.
for &(inst_imm, inst) in &[
(iadd_imm, iadd),
(imul_imm, imul),
(sdiv_imm, sdiv),
(udiv_imm, udiv),
(srem_imm, srem),
(urem_imm, urem),
(band_imm, band),
(bor_imm, bor),
(bxor_imm, bxor),
(ifcmp_imm, ifcmp),
] {
expand.legalize(
def!(a = inst_imm(x, y)),
vec![def!(a1 = iconst(y)), def!(a = inst(x, a1))],
);
}
expand.legalize(
def!(a = irsub_imm(y, x)),
vec![def!(a1 = iconst(x)), def!(a = isub(a1, y))],
);
// Rotates and shifts.
for &(inst_imm, inst) in &[
(rotl_imm, rotl),
(rotr_imm, rotr),
(ishl_imm, ishl),
(sshr_imm, sshr),
(ushr_imm, ushr),
] {
expand.legalize(
def!(a = inst_imm(x, y)),
vec![def!(a1 = iconst.I32(y)), def!(a = inst(x, a1))],
);
}
expand.legalize(
def!(a = icmp_imm(cc, x, y)),
vec![def!(a1 = iconst(y)), def!(a = icmp(cc, x, a1))],
);
//# Expansions for *_not variants of bitwise ops.
for &(inst_not, inst) in &[(band_not, band), (bor_not, bor), (bxor_not, bxor)] {
expand.legalize(
def!(a = inst_not(x, y)),
vec![def!(a1 = bnot(y)), def!(a = inst(x, a1))],
);
}
//# Expand bnot using xor.
let minus_one = Literal::constant(imm64, -1);
expand.legalize(
def!(a = bnot(x)),
vec![def!(y = iconst(minus_one)), def!(a = bxor(x, y))],
);
//# Expand bitrev
//# Adapted from Stack Overflow.
//# https://stackoverflow.com/questions/746171/most-efficient-algorithm-for-bit-reversal-from-msb-lsb-to-lsb-msb-in-c
let imm64_1 = Literal::constant(imm64, 1);
let imm64_2 = Literal::constant(imm64, 2);
let imm64_4 = Literal::constant(imm64, 4);
widen.legalize(
def!(a = bitrev.I8(x)),
vec![
def!(a1 = band_imm(x, Literal::constant(imm64, 0xaa))),
def!(a2 = ushr_imm(a1, imm64_1)),
def!(a3 = band_imm(x, Literal::constant(imm64, 0x55))),
def!(a4 = ishl_imm(a3, imm64_1)),
def!(b = bor(a2, a4)),
def!(b1 = band_imm(b, Literal::constant(imm64, 0xcc))),
def!(b2 = ushr_imm(b1, imm64_2)),
def!(b3 = band_imm(b, Literal::constant(imm64, 0x33))),
def!(b4 = ishl_imm(b3, imm64_2)),
def!(c = bor(b2, b4)),
def!(c1 = band_imm(c, Literal::constant(imm64, 0xf0))),
def!(c2 = ushr_imm(c1, imm64_4)),
def!(c3 = band_imm(c, Literal::constant(imm64, 0x0f))),
def!(c4 = ishl_imm(c3, imm64_4)),
def!(a = bor(c2, c4)),
],
);
let imm64_8 = Literal::constant(imm64, 8);
widen.legalize(
def!(a = bitrev.I16(x)),
vec![
def!(a1 = band_imm(x, Literal::constant(imm64, 0xaaaa))),
def!(a2 = ushr_imm(a1, imm64_1)),
def!(a3 = band_imm(x, Literal::constant(imm64, 0x5555))),
def!(a4 = ishl_imm(a3, imm64_1)),
def!(b = bor(a2, a4)),
def!(b1 = band_imm(b, Literal::constant(imm64, 0xcccc))),
def!(b2 = ushr_imm(b1, imm64_2)),
def!(b3 = band_imm(b, Literal::constant(imm64, 0x3333))),
def!(b4 = ishl_imm(b3, imm64_2)),
def!(c = bor(b2, b4)),
def!(c1 = band_imm(c, Literal::constant(imm64, 0xf0f0))),
def!(c2 = ushr_imm(c1, imm64_4)),
def!(c3 = band_imm(c, Literal::constant(imm64, 0x0f0f))),
def!(c4 = ishl_imm(c3, imm64_4)),
def!(d = bor(c2, c4)),
def!(d1 = band_imm(d, Literal::constant(imm64, 0xff00))),
def!(d2 = ushr_imm(d1, imm64_8)),
def!(d3 = band_imm(d, Literal::constant(imm64, 0x00ff))),
def!(d4 = ishl_imm(d3, imm64_8)),
def!(a = bor(d2, d4)),
],
);
let imm64_16 = Literal::constant(imm64, 16);
expand.legalize(
def!(a = bitrev.I32(x)),
vec![
def!(a1 = band_imm(x, Literal::constant(imm64, 0xaaaaaaaa))),
def!(a2 = ushr_imm(a1, imm64_1)),
def!(a3 = band_imm(x, Literal::constant(imm64, 0x55555555))),
def!(a4 = ishl_imm(a3, imm64_1)),
def!(b = bor(a2, a4)),
def!(b1 = band_imm(b, Literal::constant(imm64, 0xcccccccc))),
def!(b2 = ushr_imm(b1, imm64_2)),
def!(b3 = band_imm(b, Literal::constant(imm64, 0x33333333))),
def!(b4 = ishl_imm(b3, imm64_2)),
def!(c = bor(b2, b4)),
def!(c1 = band_imm(c, Literal::constant(imm64, 0xf0f0f0f0))),
def!(c2 = ushr_imm(c1, imm64_4)),
def!(c3 = band_imm(c, Literal::constant(imm64, 0x0f0f0f0f))),
def!(c4 = ishl_imm(c3, imm64_4)),
def!(d = bor(c2, c4)),
def!(d1 = band_imm(d, Literal::constant(imm64, 0xff00ff00))),
def!(d2 = ushr_imm(d1, imm64_8)),
def!(d3 = band_imm(d, Literal::constant(imm64, 0x00ff00ff))),
def!(d4 = ishl_imm(d3, imm64_8)),
def!(e = bor(d2, d4)),
def!(e1 = ushr_imm(e, imm64_16)),
def!(e2 = ishl_imm(e, imm64_16)),
def!(a = bor(e1, e2)),
],
);
#[allow(overflowing_literals)]
let imm64_0xaaaaaaaaaaaaaaaa = Literal::constant(imm64, 0xaaaaaaaaaaaaaaaa);
let imm64_0x5555555555555555 = Literal::constant(imm64, 0x5555555555555555);
#[allow(overflowing_literals)]
let imm64_0xcccccccccccccccc = Literal::constant(imm64, 0xcccccccccccccccc);
let imm64_0x3333333333333333 = Literal::constant(imm64, 0x3333333333333333);
#[allow(overflowing_literals)]
let imm64_0xf0f0f0f0f0f0f0f0 = Literal::constant(imm64, 0xf0f0f0f0f0f0f0f0);
let imm64_0x0f0f0f0f0f0f0f0f = Literal::constant(imm64, 0x0f0f0f0f0f0f0f0f);
#[allow(overflowing_literals)]
let imm64_0xff00ff00ff00ff00 = Literal::constant(imm64, 0xff00ff00ff00ff00);
let imm64_0x00ff00ff00ff00ff = Literal::constant(imm64, 0x00ff00ff00ff00ff);
#[allow(overflowing_literals)]
let imm64_0xffff0000ffff0000 = Literal::constant(imm64, 0xffff0000ffff0000);
let imm64_0x0000ffff0000ffff = Literal::constant(imm64, 0x0000ffff0000ffff);
let imm64_32 = Literal::constant(imm64, 32);
expand.legalize(
def!(a = bitrev.I64(x)),
vec![
def!(a1 = band_imm(x, imm64_0xaaaaaaaaaaaaaaaa)),
def!(a2 = ushr_imm(a1, imm64_1)),
def!(a3 = band_imm(x, imm64_0x5555555555555555)),
def!(a4 = ishl_imm(a3, imm64_1)),
def!(b = bor(a2, a4)),
def!(b1 = band_imm(b, imm64_0xcccccccccccccccc)),
def!(b2 = ushr_imm(b1, imm64_2)),
def!(b3 = band_imm(b, imm64_0x3333333333333333)),
def!(b4 = ishl_imm(b3, imm64_2)),
def!(c = bor(b2, b4)),
def!(c1 = band_imm(c, imm64_0xf0f0f0f0f0f0f0f0)),
def!(c2 = ushr_imm(c1, imm64_4)),
def!(c3 = band_imm(c, imm64_0x0f0f0f0f0f0f0f0f)),
def!(c4 = ishl_imm(c3, imm64_4)),
def!(d = bor(c2, c4)),
def!(d1 = band_imm(d, imm64_0xff00ff00ff00ff00)),
def!(d2 = ushr_imm(d1, imm64_8)),
def!(d3 = band_imm(d, imm64_0x00ff00ff00ff00ff)),
def!(d4 = ishl_imm(d3, imm64_8)),
def!(e = bor(d2, d4)),
def!(e1 = band_imm(e, imm64_0xffff0000ffff0000)),
def!(e2 = ushr_imm(e1, imm64_16)),
def!(e3 = band_imm(e, imm64_0x0000ffff0000ffff)),
def!(e4 = ishl_imm(e3, imm64_16)),
def!(f = bor(e2, e4)),
def!(f1 = ushr_imm(f, imm64_32)),
def!(f2 = ishl_imm(f, imm64_32)),
def!(a = bor(f1, f2)),
],
);
// Floating-point sign manipulations.
for &(ty, const_inst, minus_zero) in &[
(F32, f32const, &Literal::bits(ieee32, 0x80000000)),
(F64, f64const, &Literal::bits(ieee64, 0x8000000000000000)),
] {
expand.legalize(
def!(a = fabs.ty(x)),
vec![def!(b = const_inst(minus_zero)), def!(a = band_not(x, b))],
);
expand.legalize(
def!(a = fneg.ty(x)),
vec![def!(b = const_inst(minus_zero)), def!(a = bxor(x, b))],
);
expand.legalize(
def!(a = fcopysign.ty(x, y)),
vec![
def!(b = const_inst(minus_zero)),
def!(a1 = band_not(x, b)),
def!(a2 = band(y, b)),
def!(a = bor(a1, a2)),
],
);
}
expand.custom_legalize(br_icmp, "expand_br_icmp");
let mut groups = TransformGroups::new();
narrow.finish_and_add_to(&mut groups);
let expand_id = expand.finish_and_add_to(&mut groups);
// Expansions using CPU flags.
let mut expand_flags = TransformGroupBuilder::new(
"expand_flags",
r#"
Instruction expansions for architectures with flags.
Expand some instructions using CPU flags, then fall back to the normal
expansions. Not all architectures support CPU flags, so these patterns
are kept separate.
"#,
)
.chain_with(expand_id);
let imm64_0 = Literal::constant(imm64, 0);
let intcc_ne = Literal::enumerator_for(intcc, "ne");
let intcc_eq = Literal::enumerator_for(intcc, "eq");
expand_flags.legalize(
def!(trapnz(x, c)),
vec![
def!(a = ifcmp_imm(x, imm64_0)),
def!(trapif(intcc_ne, a, c)),
],
);
expand_flags.legalize(
def!(trapz(x, c)),
vec![
def!(a = ifcmp_imm(x, imm64_0)),
def!(trapif(intcc_eq, a, c)),
],
);
expand_flags.finish_and_add_to(&mut groups);
// XXX The order of declarations unfortunately matters to be compatible with the Python code.
// When it's all migrated, we can put this next to the narrow/expand finish_and_add_to calls
// above.
widen.finish_and_add_to(&mut groups);
groups
}

View File

@ -4,7 +4,6 @@ pub mod entities;
pub mod formats;
pub mod immediates;
pub mod instructions;
pub mod legalize;
pub mod settings;
pub mod types;
@ -12,14 +11,12 @@ use crate::cdsl::formats::FormatRegistry;
use crate::cdsl::inst::InstructionGroup;
use crate::cdsl::operands::OperandKind;
use crate::cdsl::settings::SettingGroup;
use crate::cdsl::xform::TransformGroups;
pub struct Definitions {
pub settings: SettingGroup,
pub instructions: InstructionGroup,
pub operand_kinds: OperandKinds,
pub format_registry: FormatRegistry,
pub transform_groups: TransformGroups,
}
pub struct OperandKinds(Vec<OperandKind>);
@ -53,14 +50,10 @@ pub fn define() -> Definitions {
let immediates = OperandKinds(immediates::define());
let entities = OperandKinds(entities::define());
let format_registry = formats::define(&immediates, &entities);
let instructions = instructions::define(&format_registry, &immediates, &entities);
let transform_groups = legalize::define(&instructions, &immediates);
Definitions {
settings: settings::define(),
instructions,
instructions: instructions::define(&format_registry, &immediates, &entities),
operand_kinds: immediates,
format_registry,
transform_groups,
}
}

View File

@ -79,10 +79,10 @@ impl Formatter {
/// Get a string containing whitespace outdented one level. Used for
/// lines of code that are inside a single indented block.
fn get_outdent(&mut self) -> String {
self.indent_pop();
let s = self.get_indent();
fn _get_outdent(&mut self) -> String {
self.indent_push();
let s = self.get_indent();
self.indent_pop();
s
}
@ -98,8 +98,8 @@ impl Formatter {
}
/// Emit a line outdented one level.
pub fn outdented_line(&mut self, s: &str) {
let new_line = format!("{}{}\n", self.get_outdent(), s);
pub fn _outdented_line(&mut self, s: &str) {
let new_line = format!("{}{}", self._get_outdent(), s);
self.lines.push(new_line);
}

View File

@ -31,9 +31,6 @@ impl<'entries, T: Eq + Hash> UniqueTable<'entries, T> {
pub fn len(&self) -> usize {
self.table.len()
}
pub fn get(&self, index: usize) -> &T {
self.table[index]
}
pub fn iter(&self) -> slice::Iter<&'entries T> {
self.table.iter()
}

File diff suppressed because one or more lines are too long

View File

@ -162,19 +162,19 @@ class Var(Atom):
Values that are defined only in the destination pattern.
"""
def __init__(self, name):
# type: (str) -> None
def __init__(self, name, typevar=None):
# type: (str, TypeVar) -> None
self.name = name
# The `Def` defining this variable in a source pattern.
self.src_def = None # type: Def
# The `Def` defining this variable in a destination pattern.
self.dst_def = None # type: Def
# TypeVar representing the type of this variable.
self.typevar = None # type: TypeVar
self.typevar = typevar # type: TypeVar
# The original 'typeof(x)' type variable that was created for this Var.
# This one doesn't change. `self.typevar` above may be changed to
# another typevar by type inference.
self.original_typevar = None # type: TypeVar
self.original_typevar = self.typevar # type: TypeVar
def __str__(self):
# type: () -> str

View File

@ -55,7 +55,7 @@ pub fn magic_u32(d: u32) -> MU32 {
q1 = u32::wrapping_add(u32::wrapping_mul(2, q1), 1);
r1 = u32::wrapping_sub(u32::wrapping_mul(2, r1), nc);
} else {
q1 = u32::wrapping_mul(2, q1);
q1 = 2 * q1;
r1 = 2 * r1;
}
if r2 + 1 >= d - r2 {
@ -101,7 +101,7 @@ pub fn magic_u64(d: u64) -> MU64 {
q1 = u64::wrapping_add(u64::wrapping_mul(2, q1), 1);
r1 = u64::wrapping_sub(u64::wrapping_mul(2, r1), nc);
} else {
q1 = u64::wrapping_mul(2, q1);
q1 = 2 * q1;
r1 = 2 * r1;
}
if r2 + 1 >= d - r2 {
@ -522,129 +522,73 @@ mod tests {
#[test]
fn test_magic_generators_dont_panic() {
// The point of this is to check that the magic number generators
// don't panic with integer wraparounds, especially at boundary cases
// for their arguments. The actual results are thrown away, although
// we force `total` to be used, so that rustc can't optimise the
// entire computation away.
// Testing UP magic_u32
// don't panic with integer wraparounds, especially at boundary
// cases for their arguments. The actual results are thrown away.
let mut total: u64 = 0;
// Testing UP magic_u32
for x in 2..(200 * 1000u32) {
let m = magic_u32(x);
total = total ^ (m.mul_by as u64);
total = total + (m.shift_by as u64);
total = total + (if m.do_add { 123 } else { 456 });
total = total - (if m.do_add { 123 } else { 456 });
}
assert_eq!(total, 2481999609);
total = 0;
// Testing MIDPOINT magic_u32
for x in 0x8000_0000u32 - 10 * 1000u32..0x8000_0000u32 + 10 * 1000u32 {
let m = magic_u32(x);
total = total ^ (m.mul_by as u64);
total = total + (m.shift_by as u64);
total = total + (if m.do_add { 123 } else { 456 });
}
assert_eq!(total, 2399809723);
total = 0;
assert_eq!(total, 1747815691);
// Testing DOWN magic_u32
for x in 0..(200 * 1000u32) {
let m = magic_u32(0xFFFF_FFFFu32 - x);
total = total ^ (m.mul_by as u64);
total = total + (m.shift_by as u64);
total = total + (if m.do_add { 123 } else { 456 });
total = total - (if m.do_add { 123 } else { 456 });
}
assert_eq!(total, 271138267);
assert_eq!(total, 2210292772);
// Testing UP magic_u64
total = 0;
for x in 2..(200 * 1000u64) {
let m = magic_u64(x);
total = total ^ m.mul_by;
total = total + (m.shift_by as u64);
total = total + (if m.do_add { 123 } else { 456 });
total = total - (if m.do_add { 123 } else { 456 });
}
assert_eq!(total, 7430004086976261161);
total = 0;
// Testing MIDPOINT magic_u64
for x in 0x8000_0000_0000_0000u64 - 10 * 1000u64..0x8000_0000_0000_0000u64 + 10 * 1000u64 {
let m = magic_u64(x);
total = total ^ m.mul_by;
total = total + (m.shift_by as u64);
total = total + (if m.do_add { 123 } else { 456 });
}
assert_eq!(total, 10312117246769520603);
assert_eq!(total, 7430004084791260605);
// Testing DOWN magic_u64
total = 0;
for x in 0..(200 * 1000u64) {
let m = magic_u64(0xFFFF_FFFF_FFFF_FFFFu64 - x);
total = total ^ m.mul_by;
total = total + (m.shift_by as u64);
total = total + (if m.do_add { 123 } else { 456 });
total = total - (if m.do_add { 123 } else { 456 });
}
assert_eq!(total, 1126603594357269734);
assert_eq!(total, 7547519887519825919);
// Testing UP magic_s32
total = 0;
for x in 0..(200 * 1000i32) {
let m = magic_s32(-0x8000_0000i32 + x);
total = total ^ (m.mul_by as u64);
total = total + (m.shift_by as u64);
}
assert_eq!(total, 18446744069953376812);
total = 0;
// Testing MIDPOINT magic_s32
for x in 0..(200 * 1000i32) {
let x2 = -100 * 1000i32 + x;
if x2 != -1 && x2 != 0 && x2 != 1 {
let m = magic_s32(x2);
total = total ^ (m.mul_by as u64);
total = total + (m.shift_by as u64);
}
}
assert_eq!(total, 351839350);
assert_eq!(total, 10899224186731671235);
// Testing DOWN magic_s32
total = 0;
for x in 0..(200 * 1000i32) {
let m = magic_s32(0x7FFF_FFFFi32 - x);
total = total ^ (m.mul_by as u64);
total = total + (m.shift_by as u64);
}
assert_eq!(total, 18446744072916880714);
assert_eq!(total, 7547519887517897369);
// Testing UP magic_s64
total = 0;
for x in 0..(200 * 1000i64) {
let m = magic_s64(-0x8000_0000_0000_0000i64 + x);
total = total ^ (m.mul_by as u64);
total = total + (m.shift_by as u64);
}
assert_eq!(total, 17929885647724831014);
total = 0;
// Testing MIDPOINT magic_s64
for x in 0..(200 * 1000i64) {
let x2 = -100 * 1000i64 + x;
if x2 != -1 && x2 != 0 && x2 != 1 {
let m = magic_s64(x2);
total = total ^ (m.mul_by as u64);
total = total + (m.shift_by as u64);
}
}
assert_eq!(total, 18106042338125661964);
assert_eq!(total, 8029756891368555163);
// Testing DOWN magic_s64
total = 0;
for x in 0..(200 * 1000i64) {
let m = magic_s64(0x7FFF_FFFF_FFFF_FFFFi64 - x);
total = total ^ (m.mul_by as u64);
total = total + (m.shift_by as u64);
}
assert_eq!(total, 563301797155560970);
// Force `total` -- and hence, the entire computation -- to
// be used, so that rustc can't optimise it out.
assert_eq!(total, 7547519887532559585u64);
}
}

View File

@ -91,25 +91,6 @@ fn size_plus_maybe_sib_or_offset_for_in_reg_1(
sizing.base_size + additional_size_if(1, inst, divert, func, needs_sib_byte_or_offset)
}
/// If the value's definition is a constant immediate, returns its unpacked value, or None
/// otherwise.
fn maybe_iconst_imm(pos: &FuncCursor, value: ir::Value) -> Option<i64> {
if let ir::ValueDef::Result(inst, _) = &pos.func.dfg.value_def(value) {
if let ir::InstructionData::UnaryImm {
opcode: ir::Opcode::Iconst,
imm,
} = &pos.func.dfg[*inst]
{
let value: i64 = (*imm).into();
Some(value)
} else {
None
}
} else {
None
}
}
/// Expand the `sdiv` and `srem` instructions using `x86_sdivmodx`.
fn expand_sdivrem(
inst: ir::Inst,
@ -128,7 +109,7 @@ fn expand_sdivrem(
} => (args[0], args[1], true),
_ => panic!("Need sdiv/srem: {}", func.dfg.display_inst(inst, None)),
};
let avoid_div_traps = isa.flags().avoid_div_traps();
let old_ebb = func.layout.pp_ebb(inst);
let result = func.dfg.first_result(inst);
let ty = func.dfg.value_type(result);
@ -137,8 +118,6 @@ fn expand_sdivrem(
pos.use_srcloc(inst);
pos.func.dfg.clear_results(inst);
let avoid_div_traps = isa.flags().avoid_div_traps();
// If we can tolerate native division traps, sdiv doesn't need branching.
if !avoid_div_traps && !is_srem {
let xhi = pos.ins().sshr_imm(x, i64::from(ty.lane_bits()) - 1);
@ -147,32 +126,6 @@ fn expand_sdivrem(
return;
}
// Try to remove checks if the input value is an immediate other than 0 or -1. For these two
// immediates, we'd ideally replace conditional traps by traps, but this requires more
// manipulation of the dfg/cfg, which is out of scope here.
let (could_be_zero, could_be_minus_one) = if let Some(imm) = maybe_iconst_imm(&pos, y) {
(imm == 0, imm == -1)
} else {
(true, true)
};
// Put in an explicit division-by-zero trap if the environment requires it.
if avoid_div_traps && could_be_zero {
pos.ins().trapz(y, ir::TrapCode::IntegerDivisionByZero);
}
if !could_be_minus_one {
let xhi = pos.ins().sshr_imm(x, i64::from(ty.lane_bits()) - 1);
let reuse = if is_srem {
[None, Some(result)]
} else {
[Some(result), None]
};
pos.ins().with_results(reuse).x86_sdivmodx(x, xhi, y);
pos.remove_inst();
return;
}
// EBB handling the -1 divisor case.
let minus_one = pos.func.dfg.make_ebb();
@ -186,6 +139,11 @@ fn expand_sdivrem(
let is_m1 = pos.ins().ifcmp_imm(y, -1);
pos.ins().brif(IntCC::Equal, is_m1, minus_one, &[]);
// Put in an explicit division-by-zero trap if the environment requires it.
if avoid_div_traps {
pos.ins().trapz(y, ir::TrapCode::IntegerDivisionByZero);
}
// Now it is safe to execute the `x86_sdivmodx` instruction which will still trap on division
// by zero.
let xhi = pos.ins().sshr_imm(x, i64::from(ty.lane_bits()) - 1);
@ -248,17 +206,7 @@ fn expand_udivrem(
// Put in an explicit division-by-zero trap if the environment requires it.
if avoid_div_traps {
let zero_check = if let Some(imm) = maybe_iconst_imm(&pos, y) {
// Ideally, we'd just replace the conditional trap with a trap when the immediate is
// zero, but this requires more manipulation of the dfg/cfg, which is out of scope
// here.
imm == 0
} else {
true
};
if zero_check {
pos.ins().trapz(y, ir::TrapCode::IntegerDivisionByZero);
}
pos.ins().trapz(y, ir::TrapCode::IntegerDivisionByZero);
}
// Now it is safe to execute the `x86_udivmodx` instruction.

View File

@ -4,6 +4,8 @@
//! should be useful for already well-optimized code. More general purpose
//! early-stage optimizations can be found in the preopt crate.
#![allow(non_snake_case)]
use crate::cursor::{Cursor, FuncCursor};
use crate::divconst_magic_numbers::{magic_s32, magic_s64, magic_u32, magic_u64};
use crate::divconst_magic_numbers::{MS32, MS64, MU32, MU64};
@ -25,7 +27,7 @@ use crate::timing;
/// if `x` is a power of two, or the negation thereof, return the power along
/// with a boolean that indicates whether `x` is negative. Else return None.
#[inline]
fn i32_is_power_of_two(x: i32) -> Option<(bool, u32)> {
fn isPowerOf2_S32(x: i32) -> Option<(bool, u32)> {
// We have to special-case this because abs(x) isn't representable.
if x == -0x8000_0000 {
return Some((true, 31));
@ -37,9 +39,9 @@ fn i32_is_power_of_two(x: i32) -> Option<(bool, u32)> {
None
}
/// Same comments as for i32_is_power_of_two apply.
/// Same comments as for isPowerOf2_S64 apply.
#[inline]
fn i64_is_power_of_two(x: i64) -> Option<(bool, u32)> {
fn isPowerOf2_S64(x: i64) -> Option<(bool, u32)> {
// We have to special-case this because abs(x) isn't representable.
if x == -0x8000_0000_0000_0000 {
return Some((true, 63));
@ -51,12 +53,10 @@ fn i64_is_power_of_two(x: i64) -> Option<(bool, u32)> {
None
}
/// Representation of an instruction that can be replaced by a single division/remainder operation
/// between a left Value operand and a right immediate operand.
#[derive(Debug)]
enum DivRemByConstInfo {
DivU32(Value, u32),
DivU64(Value, u64),
DivU32(Value, u32), // In all cases, the arguments are:
DivU64(Value, u64), // left operand, right operand
DivS32(Value, i32),
DivS64(Value, i64),
RemU32(Value, u32),
@ -65,88 +65,84 @@ enum DivRemByConstInfo {
RemS64(Value, i64),
}
/// Possibly create a DivRemByConstInfo from the given components, by figuring out which, if any,
/// of the 8 cases apply, and also taking care to sanity-check the immediate.
/// Possibly create a DivRemByConstInfo from the given components, by
/// figuring out which, if any, of the 8 cases apply, and also taking care to
/// sanity-check the immediate.
fn package_up_divrem_info(
value: Value,
value_type: Type,
imm_i64: i64,
is_signed: bool,
is_rem: bool,
argL: Value,
argL_ty: Type,
argRs: i64,
isSigned: bool,
isRem: bool,
) -> Option<DivRemByConstInfo> {
let imm_u64 = imm_i64 as u64;
match (is_signed, value_type) {
(false, I32) => {
if imm_u64 < 0x1_0000_0000 {
if is_rem {
Some(DivRemByConstInfo::RemU32(value, imm_u64 as u32))
} else {
Some(DivRemByConstInfo::DivU32(value, imm_u64 as u32))
}
} else {
None
}
}
(false, I64) => {
// unsigned 64, no range constraint.
if is_rem {
Some(DivRemByConstInfo::RemU64(value, imm_u64))
} else {
Some(DivRemByConstInfo::DivU64(value, imm_u64))
}
}
(true, I32) => {
if imm_u64 <= 0x7fff_ffff || imm_u64 >= 0xffff_ffff_8000_0000 {
if is_rem {
Some(DivRemByConstInfo::RemS32(value, imm_u64 as i32))
} else {
Some(DivRemByConstInfo::DivS32(value, imm_u64 as i32))
}
} else {
None
}
}
(true, I64) => {
// signed 64, no range constraint.
if is_rem {
Some(DivRemByConstInfo::RemS64(value, imm_u64 as i64))
} else {
Some(DivRemByConstInfo::DivS64(value, imm_u64 as i64))
}
}
_ => None,
let argRu: u64 = argRs as u64;
if !isSigned && argL_ty == I32 && argRu < 0x1_0000_0000 {
let con = if isRem {
DivRemByConstInfo::RemU32
} else {
DivRemByConstInfo::DivU32
};
return Some(con(argL, argRu as u32));
}
if !isSigned && argL_ty == I64 {
// unsigned 64, no range constraint
let con = if isRem {
DivRemByConstInfo::RemU64
} else {
DivRemByConstInfo::DivU64
};
return Some(con(argL, argRu));
}
if isSigned && argL_ty == I32 && (argRu <= 0x7fff_ffff || argRu >= 0xffff_ffff_8000_0000) {
let con = if isRem {
DivRemByConstInfo::RemS32
} else {
DivRemByConstInfo::DivS32
};
return Some(con(argL, argRu as i32));
}
if isSigned && argL_ty == I64 {
// signed 64, no range constraint
let con = if isRem {
DivRemByConstInfo::RemS64
} else {
DivRemByConstInfo::DivS64
};
return Some(con(argL, argRu as i64));
}
None
}
/// Examine `inst` to see if it is a div or rem by a constant, and if so return the operands,
/// signedness, operation size and div-vs-rem-ness in a handy bundle.
/// Examine `idata` to see if it is a div or rem by a constant, and if so
/// return the operands, signedness, operation size and div-vs-rem-ness in a
/// handy bundle.
fn get_div_info(inst: Inst, dfg: &DataFlowGraph) -> Option<DivRemByConstInfo> {
if let InstructionData::BinaryImm { opcode, arg, imm } = dfg[inst] {
let (is_signed, is_rem) = match opcode {
let idata: &InstructionData = &dfg[inst];
if let InstructionData::BinaryImm { opcode, arg, imm } = *idata {
let (isSigned, isRem) = match opcode {
Opcode::UdivImm => (false, false),
Opcode::UremImm => (false, true),
Opcode::SdivImm => (true, false),
Opcode::SremImm => (true, true),
_ => return None,
_other => return None,
};
return package_up_divrem_info(arg, dfg.value_type(arg), imm.into(), is_signed, is_rem);
// Pull the operation size (type) from the left arg
let argL_ty = dfg.value_type(arg);
return package_up_divrem_info(arg, argL_ty, imm.into(), isSigned, isRem);
}
None
}
/// Actually do the transformation given a bundle containing the relevant information.
/// `divrem_info` describes a div or rem by a constant, that `pos` currently points at, and `inst`
/// is the associated instruction. `inst` is replaced by a sequence of other operations that
/// calculate the same result. Note that there are various `divrem_info` cases where we cannot do
/// any transformation, in which case `inst` is left unchanged.
/// Actually do the transformation given a bundle containing the relevant
/// information. `divrem_info` describes a div or rem by a constant, that
/// `pos` currently points at, and `inst` is the associated instruction.
/// `inst` is replaced by a sequence of other operations that calculate the
/// same result. Note that there are various `divrem_info` cases where we
/// cannot do any transformation, in which case `inst` is left unchanged.
fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCursor, inst: Inst) {
let is_rem = match *divrem_info {
let isRem = match *divrem_info {
DivRemByConstInfo::DivU32(_, _)
| DivRemByConstInfo::DivU64(_, _)
| DivRemByConstInfo::DivS32(_, _)
@ -166,7 +162,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
// U32 div by 1: identity
// U32 rem by 1: zero
DivRemByConstInfo::DivU32(n1, 1) | DivRemByConstInfo::RemU32(n1, 1) => {
if is_rem {
if isRem {
pos.func.dfg.replace(inst).iconst(I32, 0);
} else {
pos.func.dfg.replace(inst).copy(n1);
@ -181,7 +177,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
// compute k where d == 2^k
let k = d.trailing_zeros();
debug_assert!(k >= 1 && k <= 31);
if is_rem {
if isRem {
let mask = (1u64 << k) - 1;
pos.func.dfg.replace(inst).band_imm(n1, mask as i64);
} else {
@ -220,7 +216,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
}
// Now qf holds the final quotient. If necessary calculate the
// remainder instead.
if is_rem {
if isRem {
let tt = pos.ins().imul_imm(qf, d as i64);
pos.func.dfg.replace(inst).isub(n1, tt);
} else {
@ -236,7 +232,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
// U64 div by 1: identity
// U64 rem by 1: zero
DivRemByConstInfo::DivU64(n1, 1) | DivRemByConstInfo::RemU64(n1, 1) => {
if is_rem {
if isRem {
pos.func.dfg.replace(inst).iconst(I64, 0);
} else {
pos.func.dfg.replace(inst).copy(n1);
@ -251,7 +247,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
// compute k where d == 2^k
let k = d.trailing_zeros();
debug_assert!(k >= 1 && k <= 63);
if is_rem {
if isRem {
let mask = (1u64 << k) - 1;
pos.func.dfg.replace(inst).band_imm(n1, mask as i64);
} else {
@ -290,7 +286,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
}
// Now qf holds the final quotient. If necessary calculate the
// remainder instead.
if is_rem {
if isRem {
let tt = pos.ins().imul_imm(qf, d as i64);
pos.func.dfg.replace(inst).isub(n1, tt);
} else {
@ -309,7 +305,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
// S32 div by 1: identity
// S32 rem by 1: zero
DivRemByConstInfo::DivS32(n1, 1) | DivRemByConstInfo::RemS32(n1, 1) => {
if is_rem {
if isRem {
pos.func.dfg.replace(inst).iconst(I32, 0);
} else {
pos.func.dfg.replace(inst).copy(n1);
@ -317,7 +313,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
}
DivRemByConstInfo::DivS32(n1, d) | DivRemByConstInfo::RemS32(n1, d) => {
if let Some((is_negative, k)) = i32_is_power_of_two(d) {
if let Some((isNeg, k)) = isPowerOf2_S32(d) {
// k can be 31 only in the case that d is -2^31.
debug_assert!(k >= 1 && k <= 31);
let t1 = if k - 1 == 0 {
@ -327,7 +323,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
};
let t2 = pos.ins().ushr_imm(t1, (32 - k) as i64);
let t3 = pos.ins().iadd(n1, t2);
if is_rem {
if isRem {
// S32 rem by a power-of-2
let t4 = pos.ins().band_imm(t3, i32::wrapping_neg(1 << k) as i64);
// Curiously, we don't care here what the sign of d is.
@ -335,7 +331,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
} else {
// S32 div by a power-of-2
let t4 = pos.ins().sshr_imm(t3, k as i64);
if is_negative {
if isNeg {
pos.func.dfg.replace(inst).irsub_imm(t4, 0);
} else {
pos.func.dfg.replace(inst).copy(t4);
@ -364,7 +360,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
let qf = pos.ins().iadd(q3, t1);
// Now qf holds the final quotient. If necessary calculate
// the remainder instead.
if is_rem {
if isRem {
let tt = pos.ins().imul_imm(qf, d as i64);
pos.func.dfg.replace(inst).isub(n1, tt);
} else {
@ -384,7 +380,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
// S64 div by 1: identity
// S64 rem by 1: zero
DivRemByConstInfo::DivS64(n1, 1) | DivRemByConstInfo::RemS64(n1, 1) => {
if is_rem {
if isRem {
pos.func.dfg.replace(inst).iconst(I64, 0);
} else {
pos.func.dfg.replace(inst).copy(n1);
@ -392,7 +388,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
}
DivRemByConstInfo::DivS64(n1, d) | DivRemByConstInfo::RemS64(n1, d) => {
if let Some((is_negative, k)) = i64_is_power_of_two(d) {
if let Some((isNeg, k)) = isPowerOf2_S64(d) {
// k can be 63 only in the case that d is -2^63.
debug_assert!(k >= 1 && k <= 63);
let t1 = if k - 1 == 0 {
@ -402,7 +398,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
};
let t2 = pos.ins().ushr_imm(t1, (64 - k) as i64);
let t3 = pos.ins().iadd(n1, t2);
if is_rem {
if isRem {
// S64 rem by a power-of-2
let t4 = pos.ins().band_imm(t3, i64::wrapping_neg(1 << k));
// Curiously, we don't care here what the sign of d is.
@ -410,7 +406,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
} else {
// S64 div by a power-of-2
let t4 = pos.ins().sshr_imm(t3, k as i64);
if is_negative {
if isNeg {
pos.func.dfg.replace(inst).irsub_imm(t4, 0);
} else {
pos.func.dfg.replace(inst).copy(t4);
@ -439,7 +435,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
let qf = pos.ins().iadd(q3, t1);
// Now qf holds the final quotient. If necessary calculate
// the remainder instead.
if is_rem {
if isRem {
let tt = pos.ins().imul_imm(qf, d);
pos.func.dfg.replace(inst).isub(n1, tt);
} else {
@ -774,12 +770,16 @@ pub fn do_preopt(func: &mut Function, cfg: &mut ControlFlowGraph) {
// Apply basic simplifications.
simplify(&mut pos, inst);
// Try to transform divide-by-constant into simpler operations.
if let Some(divrem_info) = get_div_info(inst, &pos.func.dfg) {
//-- BEGIN -- division by constants ----------------
let mb_dri = get_div_info(inst, &pos.func.dfg);
if let Some(divrem_info) = mb_dri {
do_divrem_transformation(&divrem_info, &mut pos, inst);
continue;
}
//-- END -- division by constants ------------------
branch_opt(&mut pos, inst);
branch_order(&mut pos, cfg, ebb, inst);
}

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"8ce435ab1fc1e02e80cca53b06c7535dfbd264d7a4ed08bb3864058d43236f15","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"87679cdb53e8cbec3b1aa45afb2124727c1c059f8bd10363d27daf318a9f9a36","src/code_translator.rs":"5c6ce509d72ca72c8dbf08beb2660c7e604fb8612274fe24ffd5eb8849ba027e","src/environ/dummy.rs":"83969d9fb4505d64aa4b9cfcef7dac55d1b4e519e7871ed7569ce7999385a131","src/environ/mod.rs":"617c147485038dfd797ab0ea71b4cfa9574d95d5d5b1ca362c6b7b6a462cf577","src/environ/spec.rs":"f37f60dd7ee664446cb2ef2480c8e5c6e6685ca9c350a104b1dc35a9142062c2","src/func_translator.rs":"7df33c55f193f25436dbefabb506e9c5f40c633c14631c78f8289cc317812956","src/lib.rs":"15b3cebafa8c6c2ce3964def7afd0af30d8eee22880e6dd903134e3ced24efd3","src/module_translator.rs":"ac54c24aaa3775f72ccd16d1781be648bb0e83ea83909f933d07e86ef1879213","src/sections_translator.rs":"55290a6b5d2a71719404d4d5f08a389dbf37c053264d17f3b292a57d1bdd5b62","src/state.rs":"9e4f67900439f6aa18cfa3f16c694487374ddf42530db4504bccab0ebc360c96","src/translation_utils.rs":"50b45794018e1c471694f4f60707329213c9fb4153798a879953a479213b8a56","tests/wasm_testsuite.rs":"c6eac90ebdb6b58d8247c22e04454d95943c5ab0621084b624eb20c0ce2a96a3"},"package":null}
{"files":{"Cargo.toml":"9827446df24b295abe90539e7ccfda7c54955f1cb44e4d49c3a4e5a66bfa5fae","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"87679cdb53e8cbec3b1aa45afb2124727c1c059f8bd10363d27daf318a9f9a36","src/code_translator.rs":"96fc0bf7b2c2f0de0596724bfe72d7fbcf5db1b676721fe024f5555ddbcbb7b6","src/environ/dummy.rs":"42df6db37892ea28e9a004934599d8bbcbd62db52f787486cd81a23f1a563613","src/environ/mod.rs":"617c147485038dfd797ab0ea71b4cfa9574d95d5d5b1ca362c6b7b6a462cf577","src/environ/spec.rs":"1c22dfbf956d80cbf34a6b8087dfb38b1f73a5cf010c341b673dba4286468bfe","src/func_translator.rs":"b27debdc0d17f30ecfa7a9bf4bdeea6054966507b5d398ccd4165574da4f674a","src/lib.rs":"95183fc86a20687e547d2edbd9868681005f0c3a2ca1ae1471e2ae38098f85c6","src/module_translator.rs":"ac54c24aaa3775f72ccd16d1781be648bb0e83ea83909f933d07e86ef1879213","src/sections_translator.rs":"55290a6b5d2a71719404d4d5f08a389dbf37c053264d17f3b292a57d1bdd5b62","src/state.rs":"1b1fa08736702d062c49118fba67f0a13752b4d863c1d11abd90eeb219777a23","src/translation_utils.rs":"50b45794018e1c471694f4f60707329213c9fb4153798a879953a479213b8a56","tests/wasm_testsuite.rs":"c6eac90ebdb6b58d8247c22e04454d95943c5ab0621084b624eb20c0ce2a96a3"},"package":null}

View File

@ -27,7 +27,7 @@ target-lexicon = "0.4.0"
[features]
default = ["std"]
std = ["cranelift-codegen/std", "cranelift-frontend/std", "wasmparser/std", "failure/std"]
std = ["cranelift-codegen/std", "cranelift-frontend/std", "wasmparser/std"]
core = ["hashmap_core", "cranelift-codegen/core", "cranelift-frontend/core", "wasmparser/core"]
[badges]

View File

@ -71,7 +71,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
* `get_global` and `set_global` are handled by the environment.
***********************************************************************************/
Operator::GetGlobal { global_index } => {
let val = match state.get_global(builder.func, global_index, environ)? {
let val = match state.get_global(builder.func, global_index, environ) {
GlobalVariable::Const(val) => val,
GlobalVariable::Memory { gv, offset, ty } => {
let addr = builder.ins().global_value(environ.pointer_type(), gv);
@ -82,7 +82,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
state.push1(val);
}
Operator::SetGlobal { global_index } => {
match state.get_global(builder.func, global_index, environ)? {
match state.get_global(builder.func, global_index, environ) {
GlobalVariable::Const(_) => panic!("global #{} is a constant", global_index),
GlobalVariable::Memory { gv, offset, ty } => {
let addr = builder.ins().global_value(environ.pointer_type(), gv);
@ -137,7 +137,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
builder.ins().jump(loop_body, &[]);
state.push_loop(loop_body, next, num_return_values(ty));
builder.switch_to_block(loop_body);
environ.translate_loop_header(builder.cursor())?;
environ.translate_loop_header(builder.cursor());
}
Operator::If { ty } => {
let val = state.pop1();
@ -348,7 +348,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
* argument referring to an index in the external functions table of the module.
************************************************************************************/
Operator::Call { function_index } => {
let (fref, num_args) = state.get_direct_func(builder.func, function_index, environ)?;
let (fref, num_args) = state.get_direct_func(builder.func, function_index, environ);
let call = environ.translate_call(
builder.cursor(),
FuncIndex::from_u32(function_index),
@ -369,8 +369,8 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
Operator::CallIndirect { index, table_index } => {
// `index` is the index of the function's signature and `table_index` is the index of
// the table to search the function in.
let (sigref, num_args) = state.get_indirect_sig(builder.func, index, environ)?;
let table = state.get_table(builder.func, table_index, environ)?;
let (sigref, num_args) = state.get_indirect_sig(builder.func, index, environ);
let table = state.get_table(builder.func, table_index, environ);
let callee = state.pop1();
let call = environ.translate_call_indirect(
builder.cursor(),
@ -398,13 +398,13 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
// The WebAssembly MVP only supports one linear memory, but we expect the reserved
// argument to be a memory index.
let heap_index = MemoryIndex::from_u32(reserved);
let heap = state.get_heap(builder.func, reserved, environ)?;
let heap = state.get_heap(builder.func, reserved, environ);
let val = state.pop1();
state.push1(environ.translate_memory_grow(builder.cursor(), heap_index, heap, val)?)
}
Operator::MemorySize { reserved } => {
let heap_index = MemoryIndex::from_u32(reserved);
let heap = state.get_heap(builder.func, reserved, environ)?;
let heap = state.get_heap(builder.func, reserved, environ);
state.push1(environ.translate_memory_size(builder.cursor(), heap_index, heap)?);
}
/******************************* Load instructions ***********************************
@ -414,72 +414,72 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
Operator::I32Load8U {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Uload8, I32, builder, state, environ)?;
translate_load(offset, ir::Opcode::Uload8, I32, builder, state, environ);
}
Operator::I32Load16U {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Uload16, I32, builder, state, environ)?;
translate_load(offset, ir::Opcode::Uload16, I32, builder, state, environ);
}
Operator::I32Load8S {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Sload8, I32, builder, state, environ)?;
translate_load(offset, ir::Opcode::Sload8, I32, builder, state, environ);
}
Operator::I32Load16S {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Sload16, I32, builder, state, environ)?;
translate_load(offset, ir::Opcode::Sload16, I32, builder, state, environ);
}
Operator::I64Load8U {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Uload8, I64, builder, state, environ)?;
translate_load(offset, ir::Opcode::Uload8, I64, builder, state, environ);
}
Operator::I64Load16U {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Uload16, I64, builder, state, environ)?;
translate_load(offset, ir::Opcode::Uload16, I64, builder, state, environ);
}
Operator::I64Load8S {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Sload8, I64, builder, state, environ)?;
translate_load(offset, ir::Opcode::Sload8, I64, builder, state, environ);
}
Operator::I64Load16S {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Sload16, I64, builder, state, environ)?;
translate_load(offset, ir::Opcode::Sload16, I64, builder, state, environ);
}
Operator::I64Load32S {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Sload32, I64, builder, state, environ)?;
translate_load(offset, ir::Opcode::Sload32, I64, builder, state, environ);
}
Operator::I64Load32U {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Uload32, I64, builder, state, environ)?;
translate_load(offset, ir::Opcode::Uload32, I64, builder, state, environ);
}
Operator::I32Load {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Load, I32, builder, state, environ)?;
translate_load(offset, ir::Opcode::Load, I32, builder, state, environ);
}
Operator::F32Load {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Load, F32, builder, state, environ)?;
translate_load(offset, ir::Opcode::Load, F32, builder, state, environ);
}
Operator::I64Load {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Load, I64, builder, state, environ)?;
translate_load(offset, ir::Opcode::Load, I64, builder, state, environ);
}
Operator::F64Load {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Load, F64, builder, state, environ)?;
translate_load(offset, ir::Opcode::Load, F64, builder, state, environ);
}
/****************************** Store instructions ***********************************
* Wasm specifies an integer alignment flag but we drop it in Cranelift.
@ -497,7 +497,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
| Operator::F64Store {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_store(offset, ir::Opcode::Store, builder, state, environ)?;
translate_store(offset, ir::Opcode::Store, builder, state, environ);
}
Operator::I32Store8 {
memarg: MemoryImmediate { flags: _, offset },
@ -505,7 +505,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
| Operator::I64Store8 {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_store(offset, ir::Opcode::Istore8, builder, state, environ)?;
translate_store(offset, ir::Opcode::Istore8, builder, state, environ);
}
Operator::I32Store16 {
memarg: MemoryImmediate { flags: _, offset },
@ -513,12 +513,12 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
| Operator::I64Store16 {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_store(offset, ir::Opcode::Istore16, builder, state, environ)?;
translate_store(offset, ir::Opcode::Istore16, builder, state, environ);
}
Operator::I64Store32 {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_store(offset, ir::Opcode::Istore32, builder, state, environ)?;
translate_store(offset, ir::Opcode::Istore32, builder, state, environ);
}
/****************************** Nullary Operators ************************************/
Operator::I32Const { value } => state.push1(builder.ins().iconst(I32, i64::from(value))),
@ -1175,10 +1175,10 @@ fn translate_load<FE: FuncEnvironment + ?Sized>(
builder: &mut FunctionBuilder,
state: &mut TranslationState,
environ: &mut FE,
) -> WasmResult<()> {
) {
let addr32 = state.pop1();
// We don't yet support multiple linear memories.
let heap = state.get_heap(builder.func, 0, environ)?;
let heap = state.get_heap(builder.func, 0, environ);
let (base, offset) = get_heap_addr(heap, addr32, offset, environ.pointer_type(), builder);
// Note that we don't set `is_aligned` here, even if the load instruction's
// alignment immediate says it's aligned, because WebAssembly's immediate
@ -1188,7 +1188,6 @@ fn translate_load<FE: FuncEnvironment + ?Sized>(
.ins()
.Load(opcode, result_ty, flags, offset.into(), base);
state.push1(dfg.first_result(load));
Ok(())
}
/// Translate a store instruction.
@ -1198,19 +1197,18 @@ fn translate_store<FE: FuncEnvironment + ?Sized>(
builder: &mut FunctionBuilder,
state: &mut TranslationState,
environ: &mut FE,
) -> WasmResult<()> {
) {
let (addr32, val) = state.pop2();
let val_ty = builder.func.dfg.value_type(val);
// We don't yet support multiple linear memories.
let heap = state.get_heap(builder.func, 0, environ)?;
let heap = state.get_heap(builder.func, 0, environ);
let (base, offset) = get_heap_addr(heap, addr32, offset, environ.pointer_type(), builder);
// See the comments in `translate_load` about the flags.
let flags = MemFlags::new();
builder
.ins()
.Store(opcode, val_ty, flags, offset.into(), val, base);
Ok(())
}
fn translate_icmp(cc: IntCC, builder: &mut FunctionBuilder, state: &mut TranslationState) {

View File

@ -182,26 +182,18 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
self.mod_info.config
}
fn return_mode(&self) -> ReturnMode {
self.return_mode
}
fn make_global(
&mut self,
func: &mut ir::Function,
index: GlobalIndex,
) -> WasmResult<GlobalVariable> {
fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable {
// Just create a dummy `vmctx` global.
let offset = cast::i32((index.index() * 8) + 8).unwrap().into();
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext {});
Ok(GlobalVariable::Memory {
GlobalVariable::Memory {
gv: vmctx,
offset,
ty: self.mod_info.globals[index].entity.ty,
})
}
}
fn make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) -> WasmResult<ir::Heap> {
fn make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) -> ir::Heap {
// Create a static heap whose base address is stored at `vmctx+0`.
let addr = func.create_global_value(ir::GlobalValueData::VMContext);
let gv = func.create_global_value(ir::GlobalValueData::Load {
@ -211,7 +203,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
readonly: true,
});
Ok(func.create_heap(ir::HeapData {
func.create_heap(ir::HeapData {
base: gv,
min_size: 0.into(),
offset_guard_size: 0x8000_0000.into(),
@ -219,10 +211,10 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
bound: 0x1_0000_0000.into(),
},
index_type: I32,
}))
})
}
fn make_table(&mut self, func: &mut ir::Function, _index: TableIndex) -> WasmResult<ir::Table> {
fn make_table(&mut self, func: &mut ir::Function, _index: TableIndex) -> ir::Table {
// Create a table whose base address is stored at `vmctx+0`.
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
let base_gv = func.create_global_value(ir::GlobalValueData::Load {
@ -238,40 +230,32 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
readonly: true,
});
Ok(func.create_table(ir::TableData {
func.create_table(ir::TableData {
base_gv,
min_size: Uimm64::new(0),
bound_gv,
element_size: Uimm64::from(u64::from(self.pointer_bytes()) * 2),
index_type: I32,
}))
})
}
fn make_indirect_sig(
&mut self,
func: &mut ir::Function,
index: SignatureIndex,
) -> WasmResult<ir::SigRef> {
fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef {
// A real implementation would probably change the calling convention and add `vmctx` and
// signature index arguments.
Ok(func.import_signature(self.vmctx_sig(index)))
func.import_signature(self.vmctx_sig(index))
}
fn make_direct_func(
&mut self,
func: &mut ir::Function,
index: FuncIndex,
) -> WasmResult<ir::FuncRef> {
fn make_direct_func(&mut self, func: &mut ir::Function, index: FuncIndex) -> ir::FuncRef {
let sigidx = self.mod_info.functions[index].entity;
// A real implementation would probably add a `vmctx` argument.
// And maybe attempt some signature de-duplication.
let signature = func.import_signature(self.vmctx_sig(sigidx));
let name = get_func_name(index);
Ok(func.import_function(ir::ExtFuncData {
func.import_function(ir::ExtFuncData {
name,
signature,
colocated: false,
}))
})
}
fn translate_call_indirect(
@ -356,6 +340,10 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
) -> WasmResult<ir::Value> {
Ok(pos.ins().iconst(I32, -1))
}
fn return_mode(&self) -> ReturnMode {
self.return_mode
}
}
impl<'data> ModuleEnvironment<'data> for DummyEnvironment {

View File

@ -39,7 +39,7 @@ pub enum GlobalVariable {
///
/// When a WebAssembly function can't be translated, one of these error codes will be returned
/// to describe the failure.
#[derive(Fail, Debug)]
#[derive(Fail, Debug, PartialEq, Eq)]
pub enum WasmError {
/// The input WebAssembly code is invalid.
///
@ -67,18 +67,6 @@ pub enum WasmError {
/// [limits]: https://cranelift.readthedocs.io/en/latest/ir.html#implementation-limits
#[fail(display = "Implementation limit exceeded")]
ImplLimitExceeded,
/// Any user-defined error. Requires an std build, where failure::Error is defined.
#[cfg(feature = "std")]
#[fail(display = "User error: {}", _0)]
User(failure::Error),
}
#[cfg(feature = "std")]
impl From<failure::Error> for WasmError {
fn from(err: failure::Error) -> Self {
WasmError::User(err)
}
}
impl From<BinaryReaderError> for WasmError {
@ -122,13 +110,6 @@ pub trait FuncEnvironment {
self.target_config().pointer_bytes()
}
/// Should the code be structured to use a single `fallthrough_return` instruction at the end
/// of the function body, rather than `return` instructions as needed? This is used by VMs
/// to append custom epilogues.
fn return_mode(&self) -> ReturnMode {
ReturnMode::NormalReturns
}
/// Set up the necessary preamble definitions in `func` to access the global variable
/// identified by `index`.
///
@ -136,23 +117,19 @@ pub trait FuncEnvironment {
///
/// Return the global variable reference that should be used to access the global and the
/// WebAssembly type of the global.
fn make_global(
&mut self,
func: &mut ir::Function,
index: GlobalIndex,
) -> WasmResult<GlobalVariable>;
fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable;
/// Set up the necessary preamble definitions in `func` to access the linear memory identified
/// by `index`.
///
/// The index space covers both imported and locally declared memories.
fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> WasmResult<ir::Heap>;
fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap;
/// Set up the necessary preamble definitions in `func` to access the table identified
/// by `index`.
///
/// The index space covers both imported and locally declared tables.
fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> WasmResult<ir::Table>;
fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> ir::Table;
/// Set up a signature definition in the preamble of `func` that can be used for an indirect
/// call with signature `index`.
@ -163,11 +140,7 @@ pub trait FuncEnvironment {
///
/// The signature will only be used for indirect calls, even if the module has direct function
/// calls with the same WebAssembly type.
fn make_indirect_sig(
&mut self,
func: &mut ir::Function,
index: SignatureIndex,
) -> WasmResult<ir::SigRef>;
fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef;
/// Set up an external function definition in the preamble of `func` that can be used to
/// directly call the function `index`.
@ -180,11 +153,7 @@ pub trait FuncEnvironment {
///
/// The function's signature will only be used for direct calls, even if the module has
/// indirect calls with the same WebAssembly type.
fn make_direct_func(
&mut self,
func: &mut ir::Function,
index: FuncIndex,
) -> WasmResult<ir::FuncRef>;
fn make_direct_func(&mut self, func: &mut ir::Function, index: FuncIndex) -> ir::FuncRef;
/// Translate a `call_indirect` WebAssembly instruction at `pos`.
///
@ -257,9 +226,15 @@ pub trait FuncEnvironment {
///
/// This can be used to insert explicit interrupt or safepoint checking at
/// the beginnings of loops.
fn translate_loop_header(&mut self, _pos: FuncCursor) -> WasmResult<()> {
fn translate_loop_header(&mut self, _pos: FuncCursor) {
// By default, don't emit anything.
Ok(())
}
/// Should the code be structured to use a single `fallthrough_return` instruction at the end
/// of the function body, rather than `return` instructions as needed? This is used by VMs
/// to append custom epilogues.
fn return_mode(&self) -> ReturnMode {
ReturnMode::NormalReturns
}
}

View File

@ -5,7 +5,7 @@
//! WebAssembly module and the runtime environment.
use crate::code_translator::translate_operator;
use crate::environ::{FuncEnvironment, ReturnMode, WasmError, WasmResult};
use crate::environ::{FuncEnvironment, ReturnMode, WasmResult};
use crate::state::TranslationState;
use cranelift_codegen::entity::EntityRef;
use cranelift_codegen::ir::{self, Ebb, InstBuilder};
@ -147,7 +147,7 @@ fn parse_local_decls(
for _ in 0..local_count {
builder.set_srcloc(cur_srcloc(reader));
let (count, ty) = reader.read_local_decl(&mut locals_total)?;
declare_locals(builder, count, ty, &mut next_local)?;
declare_locals(builder, count, ty, &mut next_local);
}
Ok(())
@ -161,7 +161,7 @@ fn declare_locals(
count: u32,
wasm_type: wasmparser::Type,
next_local: &mut usize,
) -> WasmResult<()> {
) {
// All locals are initialized to 0.
use wasmparser::Type::*;
let zeroval = match wasm_type {
@ -169,7 +169,7 @@ fn declare_locals(
I64 => builder.ins().iconst(ir::types::I64, 0),
F32 => builder.ins().f32const(ir::immediates::Ieee32::with_bits(0)),
F64 => builder.ins().f64const(ir::immediates::Ieee64::with_bits(0)),
_ => return Err(WasmError::Unsupported("unsupported local type")),
_ => panic!("invalid local type"),
};
let ty = builder.func.dfg.value_type(zeroval);
@ -179,7 +179,6 @@ fn declare_locals(
builder.def_var(local, zeroval);
*next_local += 1;
}
Ok(())
}
/// Parse the function body in `reader`.

View File

@ -38,16 +38,9 @@ extern crate alloc as std;
extern crate std;
#[cfg(not(feature = "std"))]
use hashmap_core::{
hash_map::Entry::{Occupied, Vacant},
map as hash_map, HashMap,
};
use hashmap_core::{map as hash_map, HashMap};
#[cfg(feature = "std")]
use std::collections::{
hash_map,
hash_map::Entry::{Occupied, Vacant},
HashMap,
};
use std::collections::{hash_map, HashMap};
mod code_translator;
mod environ;

View File

@ -3,8 +3,8 @@
//! The `TranslationState` struct defined in this module is used to keep track of the WebAssembly
//! value and control stacks during the translation of a single function.
use super::{HashMap, Occupied, Vacant};
use crate::environ::{FuncEnvironment, GlobalVariable, WasmResult};
use super::HashMap;
use crate::environ::{FuncEnvironment, GlobalVariable};
use crate::translation_utils::{FuncIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
use cranelift_codegen::ir::{self, Ebb, Inst, Value};
use std::vec::Vec;
@ -285,12 +285,12 @@ impl TranslationState {
func: &mut ir::Function,
index: u32,
environ: &mut FE,
) -> WasmResult<GlobalVariable> {
) -> GlobalVariable {
let index = GlobalIndex::from_u32(index);
match self.globals.entry(index) {
Occupied(entry) => Ok(*entry.get()),
Vacant(entry) => Ok(*entry.insert(environ.make_global(func, index)?)),
}
*self
.globals
.entry(index)
.or_insert_with(|| environ.make_global(func, index))
}
/// Get the `Heap` reference that should be used to access linear memory `index`.
@ -300,12 +300,12 @@ impl TranslationState {
func: &mut ir::Function,
index: u32,
environ: &mut FE,
) -> WasmResult<ir::Heap> {
) -> ir::Heap {
let index = MemoryIndex::from_u32(index);
match self.heaps.entry(index) {
Occupied(entry) => Ok(*entry.get()),
Vacant(entry) => Ok(*entry.insert(environ.make_heap(func, index)?)),
}
*self
.heaps
.entry(index)
.or_insert_with(|| environ.make_heap(func, index))
}
/// Get the `Table` reference that should be used to access table `index`.
@ -315,12 +315,12 @@ impl TranslationState {
func: &mut ir::Function,
index: u32,
environ: &mut FE,
) -> WasmResult<ir::Table> {
) -> ir::Table {
let index = TableIndex::from_u32(index);
match self.tables.entry(index) {
Occupied(entry) => Ok(*entry.get()),
Vacant(entry) => Ok(*entry.insert(environ.make_table(func, index)?)),
}
*self
.tables
.entry(index)
.or_insert_with(|| environ.make_table(func, index))
}
/// Get the `SigRef` reference that should be used to make an indirect call with signature
@ -332,15 +332,12 @@ impl TranslationState {
func: &mut ir::Function,
index: u32,
environ: &mut FE,
) -> WasmResult<(ir::SigRef, usize)> {
) -> (ir::SigRef, usize) {
let index = SignatureIndex::from_u32(index);
match self.signatures.entry(index) {
Occupied(entry) => Ok(*entry.get()),
Vacant(entry) => {
let sig = environ.make_indirect_sig(func, index)?;
Ok(*entry.insert((sig, normal_args(&func.dfg.signatures[sig]))))
}
}
*self.signatures.entry(index).or_insert_with(|| {
let sig = environ.make_indirect_sig(func, index);
(sig, normal_args(&func.dfg.signatures[sig]))
})
}
/// Get the `FuncRef` reference that should be used to make a direct call to function
@ -352,16 +349,13 @@ impl TranslationState {
func: &mut ir::Function,
index: u32,
environ: &mut FE,
) -> WasmResult<(ir::FuncRef, usize)> {
) -> (ir::FuncRef, usize) {
let index = FuncIndex::from_u32(index);
match self.functions.entry(index) {
Occupied(entry) => Ok(*entry.get()),
Vacant(entry) => {
let fref = environ.make_direct_func(func, index)?;
let sig = func.dfg.ext_funcs[fref].signature;
Ok(*entry.insert((fref, normal_args(&func.dfg.signatures[sig]))))
}
}
*self.functions.entry(index).or_insert_with(|| {
let fref = environ.make_direct_func(func, index);
let sig = func.dfg.ext_funcs[fref].signature;
(fref, normal_args(&func.dfg.signatures[sig]))
})
}
}