mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-01 08:42:13 +00:00
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:
parent
2cd9914d63
commit
2a824be22f
@ -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
46
Cargo.lock
generated
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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}
|
@ -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())
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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");
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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}
|
2
third_party/rust/cranelift-wasm/Cargo.toml
vendored
2
third_party/rust/cranelift-wasm/Cargo.toml
vendored
@ -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]
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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`.
|
||||
|
11
third_party/rust/cranelift-wasm/src/lib.rs
vendored
11
third_party/rust/cranelift-wasm/src/lib.rs
vendored
@ -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;
|
||||
|
62
third_party/rust/cranelift-wasm/src/state.rs
vendored
62
third_party/rust/cranelift-wasm/src/state.rs
vendored
@ -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]))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user