Merge remote-tracking branch 'origin/main' into pch/wasi_error_handling

This commit is contained in:
Pat Hickey
2020-08-25 15:22:51 -07:00
94 changed files with 3333 additions and 2168 deletions

View File

@@ -242,6 +242,7 @@ jobs:
--features test-programs/test_programs \
--all \
--exclude lightbeam \
--exclude wasmtime-lightbeam \
--exclude peepmatic \
--exclude peepmatic-automata \
--exclude peepmatic-fuzzing \
@@ -375,6 +376,7 @@ jobs:
--release \
--all \
--exclude lightbeam \
--exclude wasmtime-lightbeam \
--exclude peepmatic \
--exclude peepmatic-automata \
--exclude peepmatic-fuzzing \

123
Cargo.lock generated
View File

@@ -56,9 +56,9 @@ checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f"
[[package]]
name = "arbitrary"
version = "0.4.5"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cb544f1057eaaff4b34f8c4dcf56fc3cd04debd291998405d135017a7c3c0f4"
checksum = "0922a3e746b5a44e111e5603feb6704e5cc959116f66737f50bb5cbd264e9d87"
dependencies = [
"derive_arbitrary",
]
@@ -414,18 +414,6 @@ dependencies = [
"serde",
]
[[package]]
name = "cranelift-faerie"
version = "0.66.0"
dependencies = [
"anyhow",
"cranelift-codegen",
"cranelift-module",
"faerie",
"goblin",
"target-lexicon",
]
[[package]]
name = "cranelift-filetests"
version = "0.66.0"
@@ -499,7 +487,7 @@ dependencies = [
"anyhow",
"cranelift-codegen",
"cranelift-module",
"object 0.20.0",
"object 0.21.1",
"target-lexicon",
]
@@ -561,7 +549,6 @@ dependencies = [
"cranelift",
"cranelift-codegen",
"cranelift-entity",
"cranelift-faerie",
"cranelift-filetests",
"cranelift-frontend",
"cranelift-interpreter",
@@ -669,9 +656,9 @@ dependencies = [
[[package]]
name = "derive_arbitrary"
version = "0.4.5"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b43185d3e7ce7dcd44a23ca761ec026359753ebf480283a571e6463853d2ef"
checksum = "d0f7c6c81276b6b8702074defbdb1938933ddf98c7f7e0dca8d9e9214dd6c730"
dependencies = [
"proc-macro2",
"quote",
@@ -825,21 +812,6 @@ version = "0.0.0"
name = "example-wasi-wasm"
version = "0.0.0"
[[package]]
name = "faerie"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfef65b0e94693295c5d2fe2506f0ee6f43465342d4b5331659936aee8b16084"
dependencies = [
"goblin",
"indexmap",
"log",
"scroll",
"string-interner",
"target-lexicon",
"thiserror",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
@@ -951,17 +923,6 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "goblin"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3081214398d39e4bd7f2c1975f0488ed04614ffdd976c6fc7a0708278552c0da"
dependencies = [
"log",
"plain",
"scroll",
]
[[package]]
name = "hashbrown"
version = "0.6.3"
@@ -1255,6 +1216,12 @@ name = "object"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
[[package]]
name = "object"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693"
dependencies = [
"crc32fast",
"indexmap",
@@ -1384,12 +1351,6 @@ dependencies = [
name = "peepmatic-traits"
version = "0.66.0"
[[package]]
name = "plain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "ppv-lite86"
version = "0.2.8"
@@ -1715,9 +1676,9 @@ dependencies = [
[[package]]
name = "regalloc"
version = "0.0.29"
version = "0.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c178c51068338acd359c6e1ed356fcffe6b6cb3c162f68f70e251ca29bfe0eba"
checksum = "2041c2d34f6ff346d6f428974f03d8bf12679b0c816bb640dc5eb1d48848d8d1"
dependencies = [
"log",
"rustc-hash",
@@ -1962,15 +1923,6 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fbd8c65873d2e06991c33399d5e4bccba6ba04743fe99e9656afa0c64137d2c"
[[package]]
name = "string-interner"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd710eadff449a1531351b0e43eb81ea404336fa2f56c777427ab0e32a4cf183"
dependencies = [
"serde",
]
[[package]]
name = "strsim"
version = "0.8.0"
@@ -2324,7 +2276,6 @@ dependencies = [
"getrandom",
"lazy_static",
"libc",
"log",
"thiserror",
"tracing",
"wig",
@@ -2334,6 +2285,16 @@ dependencies = [
"yanix",
]
[[package]]
name = "wasm-smith"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ec52a2524c15abcc827e59c1eef9cdb742b4ba57a6db6a1ccd914e357326edd"
dependencies = [
"arbitrary",
"leb128",
]
[[package]]
name = "wasmparser"
version = "0.57.0"
@@ -2442,7 +2403,7 @@ dependencies = [
"libc",
"log",
"more-asserts",
"object 0.20.0",
"object 0.21.1",
"pretty_env_logger",
"rayon",
"structopt",
@@ -2463,6 +2424,17 @@ dependencies = [
"wat",
]
[[package]]
name = "wasmtime-cranelift"
version = "0.19.0"
dependencies = [
"cranelift-codegen",
"cranelift-entity",
"cranelift-frontend",
"cranelift-wasm",
"wasmtime-environ",
]
[[package]]
name = "wasmtime-debug"
version = "0.19.0"
@@ -2470,7 +2442,7 @@ dependencies = [
"anyhow",
"gimli 0.21.0",
"more-asserts",
"object 0.20.0",
"object 0.21.1",
"target-lexicon",
"thiserror",
"wasmparser 0.59.0",
@@ -2485,11 +2457,9 @@ dependencies = [
"cfg-if",
"cranelift-codegen",
"cranelift-entity",
"cranelift-frontend",
"cranelift-wasm",
"gimli 0.21.0",
"indexmap",
"lightbeam",
"log",
"more-asserts",
"serde",
@@ -2507,6 +2477,7 @@ dependencies = [
"libfuzzer-sys",
"peepmatic-fuzzing",
"target-lexicon",
"wasm-smith",
"wasmtime",
"wasmtime-fuzzing",
]
@@ -2542,28 +2513,40 @@ dependencies = [
"gimli 0.21.0",
"log",
"more-asserts",
"object 0.20.0",
"object 0.21.1",
"rayon",
"region",
"serde",
"target-lexicon",
"thiserror",
"wasmparser 0.59.0",
"wasmtime-cranelift",
"wasmtime-debug",
"wasmtime-environ",
"wasmtime-lightbeam",
"wasmtime-obj",
"wasmtime-profiling",
"wasmtime-runtime",
"winapi",
]
[[package]]
name = "wasmtime-lightbeam"
version = "0.19.0"
dependencies = [
"cranelift-codegen",
"lightbeam",
"wasmparser 0.59.0",
"wasmtime-environ",
]
[[package]]
name = "wasmtime-obj"
version = "0.19.0"
dependencies = [
"anyhow",
"more-asserts",
"object 0.20.0",
"object 0.21.1",
"target-lexicon",
"wasmtime-debug",
"wasmtime-environ",
@@ -2630,7 +2613,7 @@ name = "wasmtime-wasi"
version = "0.19.1"
dependencies = [
"anyhow",
"log",
"tracing",
"wasi-common",
"wasmtime",
"wasmtime-runtime",
@@ -2805,7 +2788,7 @@ dependencies = [
"cfg-if",
"filetime",
"libc",
"log",
"tracing",
]
[[package]]

View File

@@ -32,7 +32,7 @@ wasmtime-wast = { path = "crates/wast", version = "0.19.0" }
wasmtime-wasi = { path = "crates/wasi", version = "0.19.0" }
wasi-common = { path = "crates/wasi-common", version = "0.19.0" }
structopt = { version = "0.3.5", features = ["color", "suggestions"] }
object = { version = "0.20", default-features = false, features = ["write"] }
object = { version = "0.21.1", default-features = false, features = ["write"] }
anyhow = "1.0.19"
target-lexicon = { version = "0.10.0", default-features = false }
pretty_env_logger = "0.4.0"
@@ -74,12 +74,7 @@ members = [
[features]
default = ["jitdump", "wasmtime/wat", "wasmtime/parallel-compilation"]
lightbeam = [
"wasmtime-environ/lightbeam",
"wasmtime-jit/lightbeam",
"wasmtime-wast/lightbeam",
"wasmtime/lightbeam",
]
lightbeam = ["wasmtime/lightbeam"]
jitdump = ["wasmtime/jitdump"]
vtune = ["wasmtime/vtune"]

View File

@@ -181,6 +181,7 @@ fn experimental_x64_should_panic(testsuite: &str, testname: &str, strategy: &str
match (testsuite, testname) {
("simd", "simd_address") => return false,
("simd", "simd_const") => return false,
("simd", "simd_f32x4_arith") => return false,
("simd", "simd_f32x4_cmp") => return false,
("simd", "simd_f64x2_arith") => return false,
@@ -195,7 +196,6 @@ fn experimental_x64_should_panic(testsuite: &str, testname: &str, strategy: &str
/// Ignore tests that aren't supported yet.
fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
let target = env::var("TARGET").unwrap();
match strategy {
#[cfg(feature = "lightbeam")]
"Lightbeam" => match (testsuite, testname) {
@@ -206,36 +206,6 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
_ => (),
},
"Cranelift" => match (testsuite, testname) {
("simd", "simd_address") => return false,
("simd", "simd_align") => return false,
("simd", "simd_bitwise") => return false,
("simd", "simd_bit_shift") => return false,
("simd", "simd_boolean") => return false,
("simd", "simd_f32x4") => return false,
("simd", "simd_f32x4_arith") => return false,
("simd", "simd_f32x4_cmp") => return false,
("simd", "simd_f64x2") => return false,
("simd", "simd_f64x2_arith") => return false,
("simd", "simd_f64x2_cmp") => return false,
("simd", "simd_i8x16_arith") => return false,
("simd", "simd_i8x16_arith2") => return false,
("simd", "simd_i8x16_cmp") => return false,
("simd", "simd_i8x16_sat_arith") => return false,
("simd", "simd_i16x8_arith") => return false,
("simd", "simd_i16x8_arith2") => return false,
("simd", "simd_i16x8_cmp") => return false,
("simd", "simd_i16x8_sat_arith") => return false,
("simd", "simd_i32x4_arith") => return false,
("simd", "simd_i32x4_arith2") => return false,
("simd", "simd_i32x4_cmp") => return false,
("simd", "simd_lane") => return false,
("simd", "simd_load_extend") => return false,
("simd", "simd_load_splat") => return false,
("simd", "simd_store") => return false,
// Most simd tests are known to fail on aarch64 for now, it's going
// to be a big chunk of work to implement them all there!
("simd", _) if target.contains("aarch64") => return true,
// TODO(#1886): Ignore reference types tests if this isn't x64,
// because Cranelift only supports reference types on x64.
("reference_types", _) => {

View File

@@ -25,7 +25,6 @@ cranelift-wasm = { path = "wasm", version = "0.66.0", optional = true }
cranelift-native = { path = "native", version = "0.66.0" }
cranelift-filetests = { path = "filetests", version = "0.66.0" }
cranelift-module = { path = "module", version = "0.66.0" }
cranelift-faerie = { path = "faerie", version = "0.66.0" }
cranelift-object = { path = "object", version = "0.66.0" }
cranelift-simplejit = { path = "simplejit", version = "0.66.0" }
cranelift-preopt = { path = "preopt", version = "0.66.0" }

View File

@@ -28,7 +28,7 @@ byteorder = { version = "1.3.2", default-features = false }
peepmatic = { path = "../peepmatic", optional = true, version = "0.66.0" }
peepmatic-traits = { path = "../peepmatic/crates/traits", optional = true, version = "0.66.0" }
peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true, version = "0.66.0" }
regalloc = "0.0.29"
regalloc = "0.0.30"
wast = { version = "22.0.0", optional = true }
# It is a goal of the cranelift-codegen crate to have minimal external dependencies.
# Please don't add any unless they are essential to the task of creating binary

View File

@@ -41,9 +41,11 @@ pub fn has_side_effect(func: &Function, inst: Inst) -> bool {
trivially_has_side_effects(opcode) || is_load_with_defined_trapping(opcode, data)
}
/// Does the given instruction have any side-effect as per [has_side_effect], or else is a load?
pub fn has_side_effect_or_load(func: &Function, inst: Inst) -> bool {
has_side_effect(func, inst) || func.dfg[inst].opcode().can_load()
/// Does the given instruction have any side-effect as per [has_side_effect], or else is a load,
/// but not the get_pinned_reg opcode?
pub fn has_lowering_side_effect(func: &Function, inst: Inst) -> bool {
let op = func.dfg[inst].opcode();
op != Opcode::GetPinnedReg && (has_side_effect(func, inst) || op.can_load())
}
/// Is the given instruction a constant value (`iconst`, `fconst`, `bconst`) that can be

View File

@@ -3,7 +3,6 @@
// Some variants are never constructed, but we still want them as options in the future.
#![allow(dead_code)]
use crate::ir;
use crate::ir::types::{F32X2, F32X4, F64X2, I16X4, I16X8, I32X2, I32X4, I64X2, I8X16, I8X8};
use crate::ir::Type;
use crate::isa::aarch64::inst::*;
@@ -647,31 +646,37 @@ impl VectorSize {
VectorSize::Size64x2 => ScalarSize::Size64,
}
}
pub fn is_128bits(&self) -> bool {
match self {
VectorSize::Size8x8 => false,
VectorSize::Size8x16 => true,
VectorSize::Size16x4 => false,
VectorSize::Size16x8 => true,
VectorSize::Size32x2 => false,
VectorSize::Size32x4 => true,
VectorSize::Size64x2 => true,
}
}
//=============================================================================
// Instruction sub-components: atomic memory update operations
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum AtomicRMWOp {
Add,
Sub,
And,
Or,
Xor,
Xchg,
pub fn widen(&self) -> VectorSize {
match self {
VectorSize::Size8x8 => VectorSize::Size16x8,
VectorSize::Size8x16 => VectorSize::Size16x8,
VectorSize::Size16x4 => VectorSize::Size32x4,
VectorSize::Size16x8 => VectorSize::Size32x4,
VectorSize::Size32x2 => VectorSize::Size64x2,
VectorSize::Size32x4 => VectorSize::Size64x2,
VectorSize::Size64x2 => unreachable!(),
}
}
impl AtomicRMWOp {
pub fn from(ir_op: ir::AtomicRmwOp) -> Self {
match ir_op {
ir::AtomicRmwOp::Add => AtomicRMWOp::Add,
ir::AtomicRmwOp::Sub => AtomicRMWOp::Sub,
ir::AtomicRmwOp::And => AtomicRMWOp::And,
ir::AtomicRmwOp::Or => AtomicRMWOp::Or,
ir::AtomicRmwOp::Xor => AtomicRMWOp::Xor,
ir::AtomicRmwOp::Xchg => AtomicRMWOp::Xchg,
pub fn halve(&self) -> VectorSize {
match self {
VectorSize::Size8x16 => VectorSize::Size8x8,
VectorSize::Size16x8 => VectorSize::Size16x4,
VectorSize::Size32x4 => VectorSize::Size32x2,
_ => *self,
}
}
}

View File

@@ -352,12 +352,12 @@ fn enc_fround(top22: u32, rd: Writable<Reg>, rn: Reg) -> u32 {
(top22 << 10) | (machreg_to_vec(rn) << 5) | machreg_to_vec(rd.to_reg())
}
fn enc_vec_rr_misc(u: u32, size: u32, bits_12_16: u32, rd: Writable<Reg>, rn: Reg) -> u32 {
debug_assert_eq!(u & 0b1, u);
fn enc_vec_rr_misc(qu: u32, size: u32, bits_12_16: u32, rd: Writable<Reg>, rn: Reg) -> u32 {
debug_assert_eq!(qu & 0b11, qu);
debug_assert_eq!(size & 0b11, size);
debug_assert_eq!(bits_12_16 & 0b11111, bits_12_16);
let bits = 0b0_1_0_01110_00_10000_00000_10_00000_00000;
bits | u << 29
let bits = 0b0_00_01110_00_10000_00000_10_00000_00000;
bits | qu << 29
| size << 22
| bits_12_16 << 12
| machreg_to_vec(rn) << 5
@@ -1090,18 +1090,18 @@ impl MachInstEmit for Inst {
}
sink.put4(enc_ldxr(ty, x27wr, x25)); // ldxr x27, [x25]
if op == AtomicRMWOp::Xchg {
if op == inst_common::AtomicRmwOp::Xchg {
// mov x28, x26
sink.put4(enc_arith_rrr(0b101_01010_00_0, 0b000000, x28wr, xzr, x26))
} else {
// add/sub/and/orr/eor x28, x27, x26
let bits_31_21 = match op {
AtomicRMWOp::Add => 0b100_01011_00_0,
AtomicRMWOp::Sub => 0b110_01011_00_0,
AtomicRMWOp::And => 0b100_01010_00_0,
AtomicRMWOp::Or => 0b101_01010_00_0,
AtomicRMWOp::Xor => 0b110_01010_00_0,
AtomicRMWOp::Xchg => unreachable!(),
inst_common::AtomicRmwOp::Add => 0b100_01011_00_0,
inst_common::AtomicRmwOp::Sub => 0b110_01011_00_0,
inst_common::AtomicRmwOp::And => 0b100_01010_00_0,
inst_common::AtomicRmwOp::Or => 0b101_01010_00_0,
inst_common::AtomicRmwOp::Xor => 0b110_01010_00_0,
inst_common::AtomicRmwOp::Xchg => unreachable!(),
};
sink.put4(enc_arith_rrr(bits_31_21, 0b000000, x28wr, x27, x26));
}
@@ -1367,13 +1367,14 @@ impl MachInstEmit for Inst {
sink.put4(enc_fpurrrr(top17, rd, rn, rm, ra));
}
&Inst::VecMisc { op, rd, rn, size } => {
let enc_size = match size {
VectorSize::Size8x16 => 0b00,
VectorSize::Size16x8 => 0b01,
VectorSize::Size32x4 => 0b10,
VectorSize::Size64x2 => 0b11,
_ => unimplemented!(),
let enc_size = match size.lane_size() {
ScalarSize::Size8 => 0b00,
ScalarSize::Size16 => 0b01,
ScalarSize::Size32 => 0b10,
ScalarSize::Size64 => 0b11,
_ => unreachable!(),
};
let q = if size.is_128bits() { 1 } else { 0 };
let (u, bits_12_16, size) = match op {
VecMisc2::Not => (0b1, 0b00101, 0b00),
VecMisc2::Neg => (0b1, 0b01011, enc_size),
@@ -1390,8 +1391,33 @@ impl MachInstEmit for Inst {
debug_assert!(size == VectorSize::Size32x4 || size == VectorSize::Size64x2);
(0b1, 0b11111, enc_size)
}
VecMisc2::Rev64 => {
debug_assert_ne!(VectorSize::Size64x2, size);
(0b0, 0b00000, enc_size)
}
VecMisc2::Shll => {
debug_assert_ne!(VectorSize::Size64x2, size);
debug_assert!(!size.is_128bits());
(0b1, 0b10011, enc_size)
}
VecMisc2::Fcvtzs => {
debug_assert!(size == VectorSize::Size32x4 || size == VectorSize::Size64x2);
(0b0, 0b11011, enc_size)
}
VecMisc2::Fcvtzu => {
debug_assert!(size == VectorSize::Size32x4 || size == VectorSize::Size64x2);
(0b1, 0b11011, enc_size)
}
VecMisc2::Scvtf => {
debug_assert!(size == VectorSize::Size32x4 || size == VectorSize::Size64x2);
(0b0, 0b11101, enc_size & 0b1)
}
VecMisc2::Ucvtf => {
debug_assert!(size == VectorSize::Size32x4 || size == VectorSize::Size64x2);
(0b1, 0b11101, enc_size & 0b1)
}
};
sink.put4(enc_vec_rr_misc(u, size, bits_12_16, rd, rn));
sink.put4(enc_vec_rr_misc((q << 1) | u, size, bits_12_16, rd, rn));
}
&Inst::VecLanes { op, rd, rn, size } => {
let (q, size) = match size {
@@ -1634,7 +1660,12 @@ impl MachInstEmit for Inst {
| machreg_to_vec(rd.to_reg()),
);
}
&Inst::VecExtend { t, rd, rn } => {
&Inst::VecExtend {
t,
rd,
rn,
high_half,
} => {
let (u, immh) = match t {
VecExtendOp::Sxtl8 => (0b0, 0b001),
VecExtendOp::Sxtl16 => (0b0, 0b010),
@@ -1645,12 +1676,39 @@ impl MachInstEmit for Inst {
};
sink.put4(
0b000_011110_0000_000_101001_00000_00000
| ((high_half as u32) << 30)
| (u << 29)
| (immh << 19)
| (machreg_to_vec(rn) << 5)
| machreg_to_vec(rd.to_reg()),
);
}
&Inst::VecMiscNarrow {
op,
rd,
rn,
size,
high_half,
} => {
let size = match size.lane_size() {
ScalarSize::Size8 => 0b00,
ScalarSize::Size16 => 0b01,
ScalarSize::Size32 => 0b10,
_ => panic!("Unexpected vector operand lane size!"),
};
let (u, bits_12_16) = match op {
VecMiscNarrowOp::Xtn => (0b0, 0b10010),
VecMiscNarrowOp::Sqxtn => (0b0, 0b10100),
VecMiscNarrowOp::Sqxtun => (0b1, 0b10010),
};
sink.put4(enc_vec_rr_misc(
((high_half as u32) << 1) | u,
size,
bits_12_16,
rd,
rn,
));
}
&Inst::VecMovElement {
rd,
rn,
@@ -1685,12 +1743,12 @@ impl MachInstEmit for Inst {
alu_op,
size,
} => {
let enc_size = match size {
VectorSize::Size8x16 => 0b00,
VectorSize::Size16x8 => 0b01,
VectorSize::Size32x4 => 0b10,
VectorSize::Size64x2 => 0b11,
_ => 0,
let enc_size = match size.lane_size() {
ScalarSize::Size8 => 0b00,
ScalarSize::Size16 => 0b01,
ScalarSize::Size32 => 0b10,
ScalarSize::Size64 => 0b11,
_ => unreachable!(),
};
let is_float = match alu_op {
VecALUOp::Fcmeq
@@ -1751,6 +1809,11 @@ impl MachInstEmit for Inst {
VecALUOp::Fmax => (0b010_01110_00_1, 0b111101),
VecALUOp::Fmin => (0b010_01110_10_1, 0b111101),
VecALUOp::Fmul => (0b011_01110_00_1, 0b110111),
VecALUOp::Addp => (0b010_01110_00_1 | enc_size << 1, 0b101111),
VecALUOp::Umlal => {
debug_assert!(!size.is_128bits());
(0b001_01110_00_1 | enc_size << 1, 0b100000)
}
};
let top11 = if is_float {
top11 | enc_float_size << 1

View File

@@ -2008,6 +2008,7 @@ fn test_aarch64_binemit() {
t: VecExtendOp::Sxtl8,
rd: writable_vreg(4),
rn: vreg(27),
high_half: false,
},
"64A7080F",
"sxtl v4.8h, v27.8b",
@@ -2017,15 +2018,17 @@ fn test_aarch64_binemit() {
t: VecExtendOp::Sxtl16,
rd: writable_vreg(17),
rn: vreg(19),
high_half: true,
},
"71A6100F",
"sxtl v17.4s, v19.4h",
"71A6104F",
"sxtl2 v17.4s, v19.8h",
));
insns.push((
Inst::VecExtend {
t: VecExtendOp::Sxtl32,
rd: writable_vreg(30),
rn: vreg(6),
high_half: false,
},
"DEA4200F",
"sxtl v30.2d, v6.2s",
@@ -2035,15 +2038,17 @@ fn test_aarch64_binemit() {
t: VecExtendOp::Uxtl8,
rd: writable_vreg(3),
rn: vreg(29),
high_half: true,
},
"A3A7082F",
"uxtl v3.8h, v29.8b",
"A3A7086F",
"uxtl2 v3.8h, v29.16b",
));
insns.push((
Inst::VecExtend {
t: VecExtendOp::Uxtl16,
rd: writable_vreg(15),
rn: vreg(12),
high_half: false,
},
"8FA5102F",
"uxtl v15.4s, v12.4h",
@@ -2053,9 +2058,10 @@ fn test_aarch64_binemit() {
t: VecExtendOp::Uxtl32,
rd: writable_vreg(28),
rn: vreg(2),
high_half: true,
},
"5CA4202F",
"uxtl v28.2d, v2.2s",
"5CA4206F",
"uxtl2 v28.2d, v2.4s",
));
insns.push((
@@ -2082,6 +2088,42 @@ fn test_aarch64_binemit() {
"mov v31.s[1], v16.s[0]",
));
insns.push((
Inst::VecMiscNarrow {
op: VecMiscNarrowOp::Xtn,
rd: writable_vreg(22),
rn: vreg(8),
size: VectorSize::Size32x2,
high_half: false,
},
"1629A10E",
"xtn v22.2s, v8.2d",
));
insns.push((
Inst::VecMiscNarrow {
op: VecMiscNarrowOp::Sqxtn,
rd: writable_vreg(31),
rn: vreg(0),
size: VectorSize::Size16x8,
high_half: true,
},
"1F48614E",
"sqxtn2 v31.8h, v0.4s",
));
insns.push((
Inst::VecMiscNarrow {
op: VecMiscNarrowOp::Sqxtun,
rd: writable_vreg(16),
rn: vreg(23),
size: VectorSize::Size8x16,
high_half: false,
},
"F02A212E",
"sqxtun v16.8b, v23.8h",
));
insns.push((
Inst::VecRRR {
alu_op: VecALUOp::Sqadd,
@@ -3066,6 +3108,53 @@ fn test_aarch64_binemit() {
"fmul v2.2d, v0.2d, v5.2d",
));
insns.push((
Inst::VecRRR {
alu_op: VecALUOp::Addp,
rd: writable_vreg(16),
rn: vreg(12),
rm: vreg(1),
size: VectorSize::Size8x16,
},
"90BD214E",
"addp v16.16b, v12.16b, v1.16b",
));
insns.push((
Inst::VecRRR {
alu_op: VecALUOp::Addp,
rd: writable_vreg(8),
rn: vreg(12),
rm: vreg(14),
size: VectorSize::Size32x4,
},
"88BDAE4E",
"addp v8.4s, v12.4s, v14.4s",
));
insns.push((
Inst::VecRRR {
alu_op: VecALUOp::Umlal,
rd: writable_vreg(9),
rn: vreg(20),
rm: vreg(17),
size: VectorSize::Size32x2,
},
"8982B12E",
"umlal v9.2d, v20.2s, v17.2s",
));
insns.push((
Inst::VecMisc {
op: VecMisc2::Not,
rd: writable_vreg(20),
rn: vreg(17),
size: VectorSize::Size8x8,
},
"345A202E",
"mvn v20.8b, v17.8b",
));
insns.push((
Inst::VecMisc {
op: VecMisc2::Not,
@@ -3077,6 +3166,17 @@ fn test_aarch64_binemit() {
"mvn v2.16b, v1.16b",
));
insns.push((
Inst::VecMisc {
op: VecMisc2::Neg,
rd: writable_vreg(3),
rn: vreg(7),
size: VectorSize::Size8x8,
},
"E3B8202E",
"neg v3.8b, v7.8b",
));
insns.push((
Inst::VecMisc {
op: VecMisc2::Neg,
@@ -3121,6 +3221,17 @@ fn test_aarch64_binemit() {
"neg v10.2d, v8.2d",
));
insns.push((
Inst::VecMisc {
op: VecMisc2::Abs,
rd: writable_vreg(3),
rn: vreg(1),
size: VectorSize::Size8x8,
},
"23B8200E",
"abs v3.8b, v1.8b",
));
insns.push((
Inst::VecMisc {
op: VecMisc2::Abs,
@@ -3198,6 +3309,94 @@ fn test_aarch64_binemit() {
"fsqrt v7.2d, v18.2d",
));
insns.push((
Inst::VecMisc {
op: VecMisc2::Rev64,
rd: writable_vreg(1),
rn: vreg(10),
size: VectorSize::Size32x4,
},
"4109A04E",
"rev64 v1.4s, v10.4s",
));
insns.push((
Inst::VecMisc {
op: VecMisc2::Shll,
rd: writable_vreg(12),
rn: vreg(5),
size: VectorSize::Size8x8,
},
"AC38212E",
"shll v12.8h, v5.8b, #8",
));
insns.push((
Inst::VecMisc {
op: VecMisc2::Shll,
rd: writable_vreg(9),
rn: vreg(1),
size: VectorSize::Size16x4,
},
"2938612E",
"shll v9.4s, v1.4h, #16",
));
insns.push((
Inst::VecMisc {
op: VecMisc2::Shll,
rd: writable_vreg(1),
rn: vreg(10),
size: VectorSize::Size32x2,
},
"4139A12E",
"shll v1.2d, v10.2s, #32",
));
insns.push((
Inst::VecMisc {
op: VecMisc2::Fcvtzs,
rd: writable_vreg(4),
rn: vreg(22),
size: VectorSize::Size32x4,
},
"C4BAA14E",
"fcvtzs v4.4s, v22.4s",
));
insns.push((
Inst::VecMisc {
op: VecMisc2::Fcvtzu,
rd: writable_vreg(29),
rn: vreg(15),
size: VectorSize::Size64x2,
},
"FDB9E16E",
"fcvtzu v29.2d, v15.2d",
));
insns.push((
Inst::VecMisc {
op: VecMisc2::Scvtf,
rd: writable_vreg(20),
rn: vreg(8),
size: VectorSize::Size32x4,
},
"14D9214E",
"scvtf v20.4s, v8.4s",
));
insns.push((
Inst::VecMisc {
op: VecMisc2::Ucvtf,
rd: writable_vreg(10),
rn: vreg(19),
size: VectorSize::Size64x2,
},
"6ADA616E",
"ucvtf v10.2d, v19.2d",
));
insns.push((
Inst::VecLanes {
op: VecLanesOp::Uminv,
@@ -4352,7 +4551,7 @@ fn test_aarch64_binemit() {
insns.push((
Inst::AtomicRMW {
ty: I16,
op: AtomicRMWOp::Xor,
op: inst_common::AtomicRmwOp::Xor,
srcloc: None,
},
"BF3B03D53B7F5F487C031ACA3C7F1848B8FFFFB5BF3B03D5",
@@ -4362,7 +4561,7 @@ fn test_aarch64_binemit() {
insns.push((
Inst::AtomicRMW {
ty: I32,
op: AtomicRMWOp::Xchg,
op: inst_common::AtomicRmwOp::Xchg,
srcloc: None,
},
"BF3B03D53B7F5F88FC031AAA3C7F1888B8FFFFB5BF3B03D5",

View File

@@ -283,6 +283,10 @@ pub enum VecALUOp {
Fmin,
/// Floating-point multiply
Fmul,
/// Add pairwise
Addp,
/// Unsigned multiply add long
Umlal,
}
/// A Vector miscellaneous operation with two registers.
@@ -300,6 +304,29 @@ pub enum VecMisc2 {
Fneg,
/// Floating-point square root
Fsqrt,
/// Reverse elements in 64-bit doublewords
Rev64,
/// Shift left long (by element size)
Shll,
/// Floating-point convert to signed integer, rounding toward zero
Fcvtzs,
/// Floating-point convert to unsigned integer, rounding toward zero
Fcvtzu,
/// Signed integer convert to floating-point
Scvtf,
/// Unsigned integer convert to floating-point
Ucvtf,
}
/// A Vector narrowing operation with two registers.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum VecMiscNarrowOp {
/// Extract Narrow
Xtn,
/// Signed saturating extract narrow
Sqxtn,
/// Signed saturating extract unsigned narrow
Sqxtun,
}
/// An operation across the lanes of vectors.
@@ -622,7 +649,7 @@ pub enum Inst {
/// x28 (wr) scratch reg; value afterwards has no meaning
AtomicRMW {
ty: Type, // I8, I16, I32 or I64
op: AtomicRMWOp,
op: inst_common::AtomicRmwOp,
srcloc: Option<SourceLoc>,
},
@@ -869,6 +896,7 @@ pub enum Inst {
t: VecExtendOp,
rd: Writable<Reg>,
rn: Reg,
high_half: bool,
},
/// Move vector element to another vector element.
@@ -880,6 +908,15 @@ pub enum Inst {
size: VectorSize,
},
/// Vector narrowing operation.
VecMiscNarrow {
op: VecMiscNarrowOp,
rd: Writable<Reg>,
rn: Reg,
size: VectorSize,
high_half: bool,
},
/// A vector ALU op.
VecRRR {
alu_op: VecALUOp,
@@ -1605,10 +1642,21 @@ fn aarch64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
collector.add_mod(rd);
collector.add_use(rn);
}
&Inst::VecMiscNarrow {
rd, rn, high_half, ..
} => {
collector.add_use(rn);
if high_half {
collector.add_mod(rd);
} else {
collector.add_def(rd);
}
}
&Inst::VecRRR {
alu_op, rd, rn, rm, ..
} => {
if alu_op == VecALUOp::Bsl {
if alu_op == VecALUOp::Bsl || alu_op == VecALUOp::Umlal {
collector.add_mod(rd);
} else {
collector.add_def(rd);
@@ -2270,6 +2318,20 @@ fn aarch64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
map_mod(mapper, rd);
map_use(mapper, rn);
}
&mut Inst::VecMiscNarrow {
ref mut rd,
ref mut rn,
high_half,
..
} => {
map_use(mapper, rn);
if high_half {
map_mod(mapper, rd);
} else {
map_def(mapper, rd);
}
}
&mut Inst::VecRRR {
alu_op,
ref mut rd,
@@ -2277,7 +2339,7 @@ fn aarch64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
ref mut rm,
..
} => {
if alu_op == VecALUOp::Bsl {
if alu_op == VecALUOp::Bsl || alu_op == VecALUOp::Umlal {
map_mod(mapper, rd);
} else {
map_def(mapper, rd);
@@ -3120,14 +3182,20 @@ impl Inst {
let rn = show_vreg_element(rn, mb_rru, 0, size);
format!("dup {}, {}", rd, rn)
}
&Inst::VecExtend { t, rd, rn } => {
let (op, dest, src) = match t {
VecExtendOp::Sxtl8 => ("sxtl", VectorSize::Size16x8, VectorSize::Size8x8),
VecExtendOp::Sxtl16 => ("sxtl", VectorSize::Size32x4, VectorSize::Size16x4),
VecExtendOp::Sxtl32 => ("sxtl", VectorSize::Size64x2, VectorSize::Size32x2),
VecExtendOp::Uxtl8 => ("uxtl", VectorSize::Size16x8, VectorSize::Size8x8),
VecExtendOp::Uxtl16 => ("uxtl", VectorSize::Size32x4, VectorSize::Size16x4),
VecExtendOp::Uxtl32 => ("uxtl", VectorSize::Size64x2, VectorSize::Size32x2),
&Inst::VecExtend { t, rd, rn, high_half } => {
let (op, dest, src) = match (t, high_half) {
(VecExtendOp::Sxtl8, false) => ("sxtl", VectorSize::Size16x8, VectorSize::Size8x8),
(VecExtendOp::Sxtl8, true) => ("sxtl2", VectorSize::Size16x8, VectorSize::Size8x16),
(VecExtendOp::Sxtl16, false) => ("sxtl", VectorSize::Size32x4, VectorSize::Size16x4),
(VecExtendOp::Sxtl16, true) => ("sxtl2", VectorSize::Size32x4, VectorSize::Size16x8),
(VecExtendOp::Sxtl32, false) => ("sxtl", VectorSize::Size64x2, VectorSize::Size32x2),
(VecExtendOp::Sxtl32, true) => ("sxtl2", VectorSize::Size64x2, VectorSize::Size32x4),
(VecExtendOp::Uxtl8, false) => ("uxtl", VectorSize::Size16x8, VectorSize::Size8x8),
(VecExtendOp::Uxtl8, true) => ("uxtl2", VectorSize::Size16x8, VectorSize::Size8x16),
(VecExtendOp::Uxtl16, false) => ("uxtl", VectorSize::Size32x4, VectorSize::Size16x4),
(VecExtendOp::Uxtl16, true) => ("uxtl2", VectorSize::Size32x4, VectorSize::Size16x8),
(VecExtendOp::Uxtl32, false) => ("uxtl", VectorSize::Size64x2, VectorSize::Size32x2),
(VecExtendOp::Uxtl32, true) => ("uxtl2", VectorSize::Size64x2, VectorSize::Size32x4),
};
let rd = show_vreg_vector(rd.to_reg(), mb_rru, dest);
let rn = show_vreg_vector(rn, mb_rru, src);
@@ -3144,6 +3212,25 @@ impl Inst {
let rn = show_vreg_element(rn, mb_rru, idx2, size);
format!("mov {}, {}", rd, rn)
}
&Inst::VecMiscNarrow { op, rd, rn, size, high_half } => {
let dest_size = if high_half {
assert!(size.is_128bits());
size
} else {
size.halve()
};
let rd = show_vreg_vector(rd.to_reg(), mb_rru, dest_size);
let rn = show_vreg_vector(rn, mb_rru, size.widen());
let op = match (op, high_half) {
(VecMiscNarrowOp::Xtn, false) => "xtn",
(VecMiscNarrowOp::Xtn, true) => "xtn2",
(VecMiscNarrowOp::Sqxtn, false) => "sqxtn",
(VecMiscNarrowOp::Sqxtn, true) => "sqxtn2",
(VecMiscNarrowOp::Sqxtun, false) => "sqxtun",
(VecMiscNarrowOp::Sqxtun, true) => "sqxtun2",
};
format!("{} {}, {}", op, rd, rn)
}
&Inst::VecRRR {
rd,
rn,
@@ -3186,25 +3273,55 @@ impl Inst {
VecALUOp::Fmax => ("fmax", size),
VecALUOp::Fmin => ("fmin", size),
VecALUOp::Fmul => ("fmul", size),
VecALUOp::Addp => ("addp", size),
VecALUOp::Umlal => ("umlal", size),
};
let rd = show_vreg_vector(rd.to_reg(), mb_rru, size);
let rd_size = if alu_op == VecALUOp::Umlal {
size.widen()
} else {
size
};
let rd = show_vreg_vector(rd.to_reg(), mb_rru, rd_size);
let rn = show_vreg_vector(rn, mb_rru, size);
let rm = show_vreg_vector(rm, mb_rru, size);
format!("{} {}, {}, {}", op, rd, rn, rm)
}
&Inst::VecMisc { op, rd, rn, size } => {
let is_shll = op == VecMisc2::Shll;
let suffix = match (is_shll, size) {
(true, VectorSize::Size8x8) => ", #8",
(true, VectorSize::Size16x4) => ", #16",
(true, VectorSize::Size32x2) => ", #32",
_ => "",
};
let (op, size) = match op {
VecMisc2::Not => ("mvn", VectorSize::Size8x16),
VecMisc2::Not => (
"mvn",
if size.is_128bits() {
VectorSize::Size8x16
} else {
VectorSize::Size8x8
},
),
VecMisc2::Neg => ("neg", size),
VecMisc2::Abs => ("abs", size),
VecMisc2::Fabs => ("fabs", size),
VecMisc2::Fneg => ("fneg", size),
VecMisc2::Fsqrt => ("fsqrt", size),
VecMisc2::Rev64 => ("rev64", size),
VecMisc2::Shll => ("shll", size),
VecMisc2::Fcvtzs => ("fcvtzs", size),
VecMisc2::Fcvtzu => ("fcvtzu", size),
VecMisc2::Scvtf => ("scvtf", size),
VecMisc2::Ucvtf => ("ucvtf", size),
};
let rd = show_vreg_vector(rd.to_reg(), mb_rru, size);
let rd_size = if is_shll { size.widen() } else { size };
let rd = show_vreg_vector(rd.to_reg(), mb_rru, rd_size);
let rn = show_vreg_vector(rn, mb_rru, size);
format!("{} {}, {}", op, rd, rn)
format!("{} {}, {}{}", op, rd, rn, suffix)
}
&Inst::VecLanes { op, rd, rn, size } => {
let op = match op {

View File

@@ -7,10 +7,11 @@
//!
//! - Floating-point immediates (FIMM instruction).
use crate::ir;
use crate::ir::condcodes::{FloatCC, IntCC};
use crate::ir::types::*;
use crate::ir::Inst as IRInst;
use crate::ir::{AtomicRmwOp, InstructionData, Opcode, TrapCode, Type};
use crate::ir::{InstructionData, Opcode, TrapCode, Type};
use crate::machinst::lower::*;
use crate::machinst::*;
use crate::CodegenResult;
@@ -348,6 +349,45 @@ fn put_input_in_rse<C: LowerCtx<I = Inst>>(
let out_ty = ctx.output_ty(insn, 0);
let out_bits = ty_bits(out_ty);
// Is this a zero-extend or sign-extend and can we handle that with a register-mode operator?
if op == Opcode::Uextend || op == Opcode::Sextend {
let sign_extend = op == Opcode::Sextend;
let inner_ty = ctx.input_ty(insn, 0);
let inner_bits = ty_bits(inner_ty);
assert!(inner_bits < out_bits);
if match (sign_extend, narrow_mode) {
// A single zero-extend or sign-extend is equal to itself.
(_, NarrowValueMode::None) => true,
// Two zero-extends or sign-extends in a row is equal to a single zero-extend or sign-extend.
(false, NarrowValueMode::ZeroExtend32) | (false, NarrowValueMode::ZeroExtend64) => {
true
}
(true, NarrowValueMode::SignExtend32) | (true, NarrowValueMode::SignExtend64) => {
true
}
// A zero-extend and a sign-extend in a row is not equal to a single zero-extend or sign-extend
(false, NarrowValueMode::SignExtend32) | (false, NarrowValueMode::SignExtend64) => {
false
}
(true, NarrowValueMode::ZeroExtend32) | (true, NarrowValueMode::ZeroExtend64) => {
false
}
} {
let extendop = match (sign_extend, inner_bits) {
(true, 8) => ExtendOp::SXTB,
(false, 8) => ExtendOp::UXTB,
(true, 16) => ExtendOp::SXTH,
(false, 16) => ExtendOp::UXTH,
(true, 32) => ExtendOp::SXTW,
(false, 32) => ExtendOp::UXTW,
_ => unreachable!(),
};
let reg =
put_input_in_reg(ctx, InsnInput { insn, input: 0 }, NarrowValueMode::None);
return ResultRSE::RegExtend(reg, extendop);
}
}
// If `out_ty` is smaller than 32 bits and we need to zero- or sign-extend,
// then get the result into a register and return an Extend-mode operand on
// that register.
@@ -355,7 +395,7 @@ fn put_input_in_rse<C: LowerCtx<I = Inst>>(
&& ((narrow_mode.is_32bit() && out_bits < 32)
|| (!narrow_mode.is_32bit() && out_bits < 64))
{
let reg = put_input_in_reg(ctx, InsnInput { insn, input: 0 }, NarrowValueMode::None);
let reg = put_input_in_reg(ctx, input, NarrowValueMode::None);
let extendop = match (narrow_mode, out_bits) {
(NarrowValueMode::SignExtend32, 1) | (NarrowValueMode::SignExtend64, 1) => {
ExtendOp::SXTB
@@ -381,28 +421,6 @@ fn put_input_in_rse<C: LowerCtx<I = Inst>>(
};
return ResultRSE::RegExtend(reg, extendop);
}
// Is this a zero-extend or sign-extend and can we handle that with a register-mode operator?
if op == Opcode::Uextend || op == Opcode::Sextend {
assert!(out_bits == 32 || out_bits == 64);
let sign_extend = op == Opcode::Sextend;
let inner_ty = ctx.input_ty(insn, 0);
let inner_bits = ty_bits(inner_ty);
assert!(inner_bits < out_bits);
let extendop = match (sign_extend, inner_bits) {
(true, 1) => ExtendOp::SXTB,
(false, 1) => ExtendOp::UXTB,
(true, 8) => ExtendOp::SXTB,
(false, 8) => ExtendOp::UXTB,
(true, 16) => ExtendOp::SXTH,
(false, 16) => ExtendOp::UXTH,
(true, 32) => ExtendOp::SXTW,
(false, 32) => ExtendOp::UXTW,
_ => unreachable!(),
};
let reg = put_input_in_reg(ctx, InsnInput { insn, input: 0 }, NarrowValueMode::None);
return ResultRSE::RegExtend(reg, extendop);
}
}
ResultRSE::from_rs(put_input_in_rs(ctx, input, narrow_mode))
@@ -1050,7 +1068,7 @@ pub(crate) fn inst_trapcode(data: &InstructionData) -> Option<TrapCode> {
}
}
pub(crate) fn inst_atomic_rmw_op(data: &InstructionData) -> Option<AtomicRmwOp> {
pub(crate) fn inst_atomic_rmw_op(data: &InstructionData) -> Option<ir::AtomicRmwOp> {
match data {
&InstructionData::AtomicRmw { op, .. } => Some(op),
_ => None,

View File

@@ -7,7 +7,7 @@ use crate::ir::Inst as IRInst;
use crate::ir::{InstructionData, Opcode, TrapCode};
use crate::machinst::lower::*;
use crate::machinst::*;
use crate::CodegenResult;
use crate::{CodegenError, CodegenResult};
use crate::isa::aarch64::abi::*;
use crate::isa::aarch64::inst::*;
@@ -21,7 +21,8 @@ use smallvec::SmallVec;
use super::lower::*;
fn is_single_word_int_ty(ty: Type) -> bool {
/// This is target-word-size dependent. And it excludes booleans and reftypes.
fn is_valid_atomic_transaction_ty(ty: Type) -> bool {
match ty {
I8 | I16 | I32 | I64 => true,
_ => false,
@@ -66,7 +67,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let rd = get_output_reg(ctx, outputs[0]);
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
let ty = ty.unwrap();
if ty_bits(ty) < 128 {
if !ty.is_vector() {
let (rm, negated) = put_input_in_rse_imm12_maybe_negated(
ctx,
inputs[1],
@@ -94,7 +95,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let rd = get_output_reg(ctx, outputs[0]);
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
let ty = ty.unwrap();
if ty_bits(ty) < 128 {
if !ty.is_vector() {
let (rm, negated) = put_input_in_rse_imm12_maybe_negated(
ctx,
inputs[1],
@@ -124,7 +125,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let is_signed = op == Opcode::SaddSat || op == Opcode::SsubSat;
let ty = ty.unwrap();
let rd = get_output_reg(ctx, outputs[0]);
if ty_bits(ty) < 128 {
if !ty.is_vector() {
let narrow_mode = if is_signed {
NarrowValueMode::SignExtend64
} else {
@@ -180,7 +181,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
Opcode::Ineg => {
let rd = get_output_reg(ctx, outputs[0]);
let ty = ty.unwrap();
if ty_bits(ty) < 128 {
if !ty.is_vector() {
let rn = zero_reg();
let rm = put_input_in_rse_imm12(ctx, inputs[0], NarrowValueMode::None);
let alu_op = choose_32_64(ty, ALUOp::Sub32, ALUOp::Sub64);
@@ -201,7 +202,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
let rm = put_input_in_reg(ctx, inputs[1], NarrowValueMode::None);
let ty = ty.unwrap();
if ty_bits(ty) < 128 {
if !ty.is_vector() {
let alu_op = choose_32_64(ty, ALUOp::MAdd32, ALUOp::MAdd64);
ctx.emit(Inst::AluRRRR {
alu_op,
@@ -210,6 +211,112 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
rm,
ra: zero_reg(),
});
} else {
if ty == I64X2 {
let tmp1 = ctx.alloc_tmp(RegClass::V128, I64X2);
let tmp2 = ctx.alloc_tmp(RegClass::V128, I64X2);
// This I64X2 multiplication is performed with several 32-bit
// operations.
// 64-bit numbers x and y, can be represented as:
// x = a + 2^32(b)
// y = c + 2^32(d)
// A 64-bit multiplication is:
// x * y = ac + 2^32(ad + bc) + 2^64(bd)
// note: `2^64(bd)` can be ignored, the value is too large to fit in
// 64 bits.
// This sequence implements a I64X2 multiply, where the registers
// `rn` and `rm` are split up into 32-bit components:
// rn = |d|c|b|a|
// rm = |h|g|f|e|
//
// rn * rm = |cg + 2^32(ch + dg)|ae + 2^32(af + be)|
//
// The sequence is:
// rev64 rd.4s, rm.4s
// mul rd.4s, rd.4s, rn.4s
// xtn tmp1.2s, rn.2d
// addp rd.4s, rd.4s, rd.4s
// xtn tmp2.2s, rm.2d
// shll rd.2d, rd.2s, #32
// umlal rd.2d, tmp2.2s, tmp1.2s
// Reverse the 32-bit elements in the 64-bit words.
// rd = |g|h|e|f|
ctx.emit(Inst::VecMisc {
op: VecMisc2::Rev64,
rd,
rn: rm,
size: VectorSize::Size32x4,
});
// Calculate the high half components.
// rd = |dg|ch|be|af|
//
// Note that this 32-bit multiply of the high half
// discards the bits that would overflow, same as
// if 64-bit operations were used. Also the Shll
// below would shift out the overflow bits anyway.
ctx.emit(Inst::VecRRR {
alu_op: VecALUOp::Mul,
rd,
rn: rd.to_reg(),
rm: rn,
size: VectorSize::Size32x4,
});
// Extract the low half components of rn.
// tmp1 = |c|a|
ctx.emit(Inst::VecMiscNarrow {
op: VecMiscNarrowOp::Xtn,
rd: tmp1,
rn,
size: VectorSize::Size32x2,
high_half: false,
});
// Sum the respective high half components.
// rd = |dg+ch|be+af||dg+ch|be+af|
ctx.emit(Inst::VecRRR {
alu_op: VecALUOp::Addp,
rd: rd,
rn: rd.to_reg(),
rm: rd.to_reg(),
size: VectorSize::Size32x4,
});
// Extract the low half components of rm.
// tmp2 = |g|e|
ctx.emit(Inst::VecMiscNarrow {
op: VecMiscNarrowOp::Xtn,
rd: tmp2,
rn: rm,
size: VectorSize::Size32x2,
high_half: false,
});
// Shift the high half components, into the high half.
// rd = |dg+ch << 32|be+af << 32|
ctx.emit(Inst::VecMisc {
op: VecMisc2::Shll,
rd,
rn: rd.to_reg(),
size: VectorSize::Size32x2,
});
// Multiply the low components together, and accumulate with the high
// half.
// rd = |rd[1] + cg|rd[0] + ae|
ctx.emit(Inst::VecRRR {
alu_op: VecALUOp::Umlal,
rd,
rn: tmp2.to_reg(),
rm: tmp1.to_reg(),
size: VectorSize::Size32x2,
});
} else {
ctx.emit(Inst::VecRRR {
alu_op: VecALUOp::Mul,
@@ -220,6 +327,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
});
}
}
}
Opcode::Umulhi | Opcode::Smulhi => {
let rd = get_output_reg(ctx, outputs[0]);
@@ -465,7 +573,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
Opcode::Bnot => {
let rd = get_output_reg(ctx, outputs[0]);
let ty = ty.unwrap();
if ty_bits(ty) < 128 {
if !ty.is_vector() {
let rm = put_input_in_rs_immlogic(ctx, inputs[0], NarrowValueMode::None);
let alu_op = choose_32_64(ty, ALUOp::OrrNot32, ALUOp::OrrNot64);
// NOT rd, rm ==> ORR_NOT rd, zero, rm
@@ -489,7 +597,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
| Opcode::BxorNot => {
let rd = get_output_reg(ctx, outputs[0]);
let ty = ty.unwrap();
if ty_bits(ty) < 128 {
if !ty.is_vector() {
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
let rm = put_input_in_rs_immlogic(ctx, inputs[1], NarrowValueMode::None);
let alu_op = match op {
@@ -528,7 +636,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
Opcode::Ishl | Opcode::Ushr | Opcode::Sshr => {
let ty = ty.unwrap();
let rd = get_output_reg(ctx, outputs[0]);
if ty_bits(ty) < 128 {
if !ty.is_vector() {
let size = OperandSize::from_bits(ty_bits(ty));
let narrow_mode = match (op, size) {
(Opcode::Ishl, _) => NarrowValueMode::None,
@@ -1054,6 +1162,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
t,
rd,
rn: rd.to_reg(),
high_half: false,
});
}
}
@@ -1120,7 +1229,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let mut r_addr = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
let mut r_arg2 = put_input_in_reg(ctx, inputs[1], NarrowValueMode::None);
let ty_access = ty.unwrap();
assert!(is_single_word_int_ty(ty_access));
assert!(is_valid_atomic_transaction_ty(ty_access));
let memflags = ctx.memflags(insn).expect("memory flags");
let srcloc = if !memflags.notrap() {
Some(ctx.srcloc(insn))
@@ -1136,7 +1245,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
ctx.emit(Inst::gen_move(Writable::from_reg(xreg(25)), r_addr, I64));
ctx.emit(Inst::gen_move(Writable::from_reg(xreg(26)), r_arg2, I64));
// Now the AtomicRMW insn itself
let op = AtomicRMWOp::from(inst_atomic_rmw_op(ctx.data(insn)).unwrap());
let op = inst_common::AtomicRmwOp::from(inst_atomic_rmw_op(ctx.data(insn)).unwrap());
ctx.emit(Inst::AtomicRMW {
ty: ty_access,
op,
@@ -1156,7 +1265,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let mut r_expected = put_input_in_reg(ctx, inputs[1], NarrowValueMode::None);
let mut r_replacement = put_input_in_reg(ctx, inputs[2], NarrowValueMode::None);
let ty_access = ty.unwrap();
assert!(is_single_word_int_ty(ty_access));
assert!(is_valid_atomic_transaction_ty(ty_access));
let memflags = ctx.memflags(insn).expect("memory flags");
let srcloc = if !memflags.notrap() {
Some(ctx.srcloc(insn))
@@ -1194,7 +1303,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let r_data = get_output_reg(ctx, outputs[0]);
let r_addr = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
let ty_access = ty.unwrap();
assert!(is_single_word_int_ty(ty_access));
assert!(is_valid_atomic_transaction_ty(ty_access));
let memflags = ctx.memflags(insn).expect("memory flags");
let srcloc = if !memflags.notrap() {
Some(ctx.srcloc(insn))
@@ -1213,7 +1322,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let r_data = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
let r_addr = put_input_in_reg(ctx, inputs[1], NarrowValueMode::None);
let ty_access = ctx.input_ty(insn, 0);
assert!(is_single_word_int_ty(ty_access));
assert!(is_valid_atomic_transaction_ty(ty_access));
let memflags = ctx.memflags(insn).expect("memory flags");
let srcloc = if !memflags.notrap() {
Some(ctx.srcloc(insn))
@@ -1328,7 +1437,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
Opcode::Bitselect | Opcode::Vselect => {
let ty = ty.unwrap();
if ty_bits(ty) < 128 {
if !ty.is_vector() {
debug_assert_ne!(Opcode::Vselect, op);
let tmp = ctx.alloc_tmp(RegClass::I64, I64);
let rd = get_output_reg(ctx, outputs[0]);
@@ -1591,7 +1700,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
};
let rn = put_input_in_reg(ctx, inputs[0], narrow_mode);
if ty_bits(ty) < 128 {
if !ty.is_vector() {
let alu_op = choose_32_64(ty, ALUOp::SubS32, ALUOp::SubS64);
let rm = put_input_in_rse_imm12(ctx, inputs[1], narrow_mode);
ctx.emit(alu_inst_imm12(alu_op, writable_zero_reg(), rn, rm));
@@ -1611,7 +1720,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let rm = put_input_in_reg(ctx, inputs[1], NarrowValueMode::None);
let rd = get_output_reg(ctx, outputs[0]);
if ty_bits(ty) < 128 {
if !ty.is_vector() {
match ty_bits(ty) {
32 => {
ctx.emit(Inst::FpuCmp32 { rn, rm });
@@ -2001,7 +2110,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
let rm = put_input_in_reg(ctx, inputs[1], NarrowValueMode::None);
let rd = get_output_reg(ctx, outputs[0]);
if bits < 128 {
if !ty.is_vector() {
let fpu_op = match (op, bits) {
(Opcode::Fadd, 32) => FPUOp2::Add32,
(Opcode::Fadd, 64) => FPUOp2::Add64,
@@ -2044,7 +2153,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let bits = ty_bits(ty);
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
let rd = get_output_reg(ctx, outputs[0]);
if bits < 128 {
if !ty.is_vector() {
let fpu_op = match (op, bits) {
(Opcode::Sqrt, 32) => FPUOp1::Sqrt32,
(Opcode::Sqrt, 64) => FPUOp1::Sqrt64,
@@ -2157,12 +2266,12 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let out_bits = ty_bits(ctx.output_ty(insn, 0));
let signed = op == Opcode::FcvtToSint;
let op = match (signed, in_bits, out_bits) {
(false, 32, 32) => FpuToIntOp::F32ToU32,
(true, 32, 32) => FpuToIntOp::F32ToI32,
(false, 32, 8) | (false, 32, 16) | (false, 32, 32) => FpuToIntOp::F32ToU32,
(true, 32, 8) | (true, 32, 16) | (true, 32, 32) => FpuToIntOp::F32ToI32,
(false, 32, 64) => FpuToIntOp::F32ToU64,
(true, 32, 64) => FpuToIntOp::F32ToI64,
(false, 64, 32) => FpuToIntOp::F64ToU32,
(true, 64, 32) => FpuToIntOp::F64ToI32,
(false, 64, 8) | (false, 64, 16) | (false, 64, 32) => FpuToIntOp::F64ToU32,
(true, 64, 8) | (true, 64, 16) | (true, 64, 32) => FpuToIntOp::F64ToI32,
(false, 64, 64) => FpuToIntOp::F64ToU64,
(true, 64, 64) => FpuToIntOp::F64ToI64,
_ => panic!("Unknown input/output-bits combination"),
@@ -2199,6 +2308,16 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
if in_bits == 32 {
// From float32.
let (low_bound, low_cond, high_bound) = match (signed, out_bits) {
(true, 8) => (
i8::min_value() as f32 - 1.,
FloatCC::GreaterThan,
i8::max_value() as f32 + 1.,
),
(true, 16) => (
i16::min_value() as f32 - 1.,
FloatCC::GreaterThan,
i16::max_value() as f32 + 1.,
),
(true, 32) => (
i32::min_value() as f32, // I32_MIN - 1 isn't precisely representable as a f32.
FloatCC::GreaterThanOrEqual,
@@ -2209,6 +2328,8 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
FloatCC::GreaterThanOrEqual,
i64::max_value() as f32 + 1.,
),
(false, 8) => (-1., FloatCC::GreaterThan, u8::max_value() as f32 + 1.),
(false, 16) => (-1., FloatCC::GreaterThan, u16::max_value() as f32 + 1.),
(false, 32) => (-1., FloatCC::GreaterThan, u32::max_value() as f32 + 1.),
(false, 64) => (-1., FloatCC::GreaterThan, u64::max_value() as f32 + 1.),
_ => panic!("Unknown input/output-bits combination"),
@@ -2240,6 +2361,16 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
} else {
// From float64.
let (low_bound, low_cond, high_bound) = match (signed, out_bits) {
(true, 8) => (
i8::min_value() as f64 - 1.,
FloatCC::GreaterThan,
i8::max_value() as f64 + 1.,
),
(true, 16) => (
i16::min_value() as f64 - 1.,
FloatCC::GreaterThan,
i16::max_value() as f64 + 1.,
),
(true, 32) => (
i32::min_value() as f64 - 1.,
FloatCC::GreaterThan,
@@ -2250,6 +2381,8 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
FloatCC::GreaterThanOrEqual,
i64::max_value() as f64 + 1.,
),
(false, 8) => (-1., FloatCC::GreaterThan, u8::max_value() as f64 + 1.),
(false, 16) => (-1., FloatCC::GreaterThan, u16::max_value() as f64 + 1.),
(false, 32) => (-1., FloatCC::GreaterThan, u32::max_value() as f64 + 1.),
(false, 64) => (-1., FloatCC::GreaterThan, u64::max_value() as f64 + 1.),
_ => panic!("Unknown input/output-bits combination"),
@@ -2285,14 +2418,32 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
}
Opcode::FcvtFromUint | Opcode::FcvtFromSint => {
let in_bits = ty_bits(ctx.input_ty(insn, 0));
let out_bits = ty_bits(ctx.output_ty(insn, 0));
let ty = ty.unwrap();
let signed = op == Opcode::FcvtFromSint;
let rd = get_output_reg(ctx, outputs[0]);
if ty.is_vector() {
let op = if signed {
VecMisc2::Scvtf
} else {
VecMisc2::Ucvtf
};
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
ctx.emit(Inst::VecMisc {
op,
rd,
rn,
size: VectorSize::from_ty(ty),
});
} else {
let in_bits = ty_bits(ctx.input_ty(insn, 0));
let out_bits = ty_bits(ty);
let op = match (signed, in_bits, out_bits) {
(false, 32, 32) => IntToFpuOp::U32ToF32,
(true, 32, 32) => IntToFpuOp::I32ToF32,
(false, 32, 64) => IntToFpuOp::U32ToF64,
(true, 32, 64) => IntToFpuOp::I32ToF64,
(false, 8, 32) | (false, 16, 32) | (false, 32, 32) => IntToFpuOp::U32ToF32,
(true, 8, 32) | (true, 16, 32) | (true, 32, 32) => IntToFpuOp::I32ToF32,
(false, 8, 64) | (false, 16, 64) | (false, 32, 64) => IntToFpuOp::U32ToF64,
(true, 8, 64) | (true, 16, 64) | (true, 32, 64) => IntToFpuOp::I32ToF64,
(false, 64, 32) => IntToFpuOp::U64ToF32,
(true, 64, 32) => IntToFpuOp::I64ToF32,
(false, 64, 64) => IntToFpuOp::U64ToF64,
@@ -2300,26 +2451,40 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
_ => panic!("Unknown input/output-bits combination"),
};
let narrow_mode = match (signed, in_bits) {
(false, 32) => NarrowValueMode::ZeroExtend32,
(true, 32) => NarrowValueMode::SignExtend32,
(false, 8) | (false, 16) | (false, 32) => NarrowValueMode::ZeroExtend32,
(true, 8) | (true, 16) | (true, 32) => NarrowValueMode::SignExtend32,
(false, 64) => NarrowValueMode::ZeroExtend64,
(true, 64) => NarrowValueMode::SignExtend64,
_ => panic!("Unknown input size"),
};
let rn = put_input_in_reg(ctx, inputs[0], narrow_mode);
let rd = get_output_reg(ctx, outputs[0]);
ctx.emit(Inst::IntToFpu { op, rd, rn });
}
}
Opcode::FcvtToUintSat | Opcode::FcvtToSintSat => {
let in_ty = ctx.input_ty(insn, 0);
let in_bits = ty_bits(in_ty);
let out_ty = ctx.output_ty(insn, 0);
let out_bits = ty_bits(out_ty);
let ty = ty.unwrap();
let out_signed = op == Opcode::FcvtToSintSat;
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
let rd = get_output_reg(ctx, outputs[0]);
if ty.is_vector() {
let op = if out_signed {
VecMisc2::Fcvtzs
} else {
VecMisc2::Fcvtzu
};
ctx.emit(Inst::VecMisc {
op,
rd,
rn,
size: VectorSize::from_ty(ty),
});
} else {
let in_ty = ctx.input_ty(insn, 0);
let in_bits = ty_bits(in_ty);
let out_bits = ty_bits(ty);
// FIMM Vtmp1, u32::MAX or u64::MAX or i32::MAX or i64::MAX
// FMIN Vtmp2, Vin, Vtmp1
// FIMM Vtmp1, 0 or 0 or i32::MIN or i64::MIN
@@ -2433,6 +2598,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
rn: rtmp2.to_reg(),
});
}
}
Opcode::IaddIfcout => {
// This is a two-output instruction that is needed for the
@@ -2560,12 +2726,62 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
});
}
Opcode::Snarrow
| Opcode::Unarrow
| Opcode::SwidenLow
| Opcode::SwidenHigh
| Opcode::UwidenLow
| Opcode::UwidenHigh => unimplemented!(),
Opcode::Snarrow | Opcode::Unarrow => {
let op = if op == Opcode::Snarrow {
VecMiscNarrowOp::Sqxtn
} else {
VecMiscNarrowOp::Sqxtun
};
let rd = get_output_reg(ctx, outputs[0]);
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
let rn2 = put_input_in_reg(ctx, inputs[1], NarrowValueMode::None);
let ty = ty.unwrap();
ctx.emit(Inst::VecMiscNarrow {
op,
rd,
rn,
size: VectorSize::from_ty(ty),
high_half: false,
});
ctx.emit(Inst::VecMiscNarrow {
op,
rd,
rn: rn2,
size: VectorSize::from_ty(ty),
high_half: true,
});
}
Opcode::SwidenLow | Opcode::SwidenHigh | Opcode::UwidenLow | Opcode::UwidenHigh => {
let lane_type = ty.unwrap().lane_type();
let rd = get_output_reg(ctx, outputs[0]);
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
let (t, high_half) = match (lane_type, op) {
(I16, Opcode::SwidenLow) => (VecExtendOp::Sxtl8, false),
(I16, Opcode::SwidenHigh) => (VecExtendOp::Sxtl8, true),
(I16, Opcode::UwidenLow) => (VecExtendOp::Uxtl8, false),
(I16, Opcode::UwidenHigh) => (VecExtendOp::Uxtl8, true),
(I32, Opcode::SwidenLow) => (VecExtendOp::Sxtl16, false),
(I32, Opcode::SwidenHigh) => (VecExtendOp::Sxtl16, true),
(I32, Opcode::UwidenLow) => (VecExtendOp::Uxtl16, false),
(I32, Opcode::UwidenHigh) => (VecExtendOp::Uxtl16, true),
_ => {
return Err(CodegenError::Unsupported(format!(
"Unsupported SIMD vector lane type: {:?}",
lane_type
)));
}
};
ctx.emit(Inst::VecExtend {
t,
rd,
rn,
high_half,
});
}
Opcode::TlsValue => unimplemented!(),
}

View File

@@ -325,10 +325,13 @@ impl ABIBody for X64ABIBody {
self.fp_to_arg_offset() + off <= u32::max_value() as i64,
"large offset nyi"
);
load_stack(
Amode::imm_reg((self.fp_to_arg_offset() + off) as u32, regs::rbp()),
to_reg,
let from_addr = Amode::imm_reg((self.fp_to_arg_offset() + off) as u32, regs::rbp());
Inst::load(
ty,
from_addr,
to_reg,
ExtKind::ZeroExtend,
/* infallible load */ None,
)
}
}
@@ -420,8 +423,10 @@ impl ABIBody for X64ABIBody {
"large stack return offset nyi"
);
let mem = Amode::imm_reg(off as u32, self.ret_area_ptr.unwrap().to_reg());
ret.push(store_stack(mem, from_reg.to_reg(), ty))
let from_reg = from_reg.to_reg();
let to_mem = Amode::imm_reg(off as u32, self.ret_area_ptr.unwrap().to_reg());
let store = Inst::store(ty, from_reg, to_mem, /* infallible store */ None);
ret.push(store)
}
}
@@ -464,17 +469,20 @@ impl ABIBody for X64ABIBody {
unimplemented!("store_stackslot")
}
fn load_spillslot(&self, slot: SpillSlot, ty: Type, into_reg: Writable<Reg>) -> Inst {
fn load_spillslot(&self, slot: SpillSlot, ty: Type, to_reg: Writable<Reg>) -> Inst {
// Offset from beginning of spillslot area, which is at nominal-SP + stackslots_size.
let islot = slot.get() as i64;
let spill_off = islot * 8;
let sp_off = self.stack_slots_size as i64 + spill_off;
debug_assert!(sp_off <= u32::max_value() as i64, "large spill offsets NYI");
trace!("load_spillslot: slot {:?} -> sp_off {}", slot, sp_off);
load_stack(
SyntheticAmode::nominal_sp_offset(sp_off as u32),
into_reg,
let from_addr = SyntheticAmode::nominal_sp_offset(sp_off as u32);
Inst::load(
ty,
from_addr,
to_reg,
ExtKind::ZeroExtend,
/* infallible load */ None,
)
}
@@ -485,11 +493,8 @@ impl ABIBody for X64ABIBody {
let sp_off = self.stack_slots_size as i64 + spill_off;
debug_assert!(sp_off <= u32::max_value() as i64, "large spill offsets NYI");
trace!("store_spillslot: slot {:?} -> sp_off {}", slot, sp_off);
store_stack(
SyntheticAmode::nominal_sp_offset(sp_off as u32),
from_reg,
ty,
)
let to_mem = SyntheticAmode::nominal_sp_offset(sp_off as u32);
Inst::store(ty, from_reg, to_mem, /* infallible store */ None)
}
fn spillslots_to_stack_map(&self, slots: &[SpillSlot], state: &EmitState) -> StackMap {
@@ -1003,66 +1008,6 @@ fn adjust_stack<C: LowerCtx<I = Inst>>(ctx: &mut C, amount: u64, is_sub: bool) {
}
}
fn load_stack(mem: impl Into<SyntheticAmode>, into_reg: Writable<Reg>, ty: Type) -> Inst {
let (is_int, ext_mode) = match ty {
types::B1 | types::B8 | types::I8 => (true, Some(ExtMode::BQ)),
types::B16 | types::I16 => (true, Some(ExtMode::WQ)),
types::B32 | types::I32 => (true, Some(ExtMode::LQ)),
types::B64 | types::I64 | types::R64 => (true, None),
types::F32 | types::F64 => (false, None),
_ => panic!("load_stack({})", ty),
};
let mem = mem.into();
if is_int {
match ext_mode {
Some(ext_mode) => Inst::movsx_rm_r(
ext_mode,
RegMem::mem(mem),
into_reg,
/* infallible load */ None,
),
None => Inst::mov64_m_r(mem, into_reg, None /* infallible */),
}
} else {
let sse_op = match ty {
types::F32 => SseOpcode::Movss,
types::F64 => SseOpcode::Movsd,
_ => unreachable!(),
};
Inst::xmm_mov(
sse_op,
RegMem::mem(mem),
into_reg,
None, /* infallible */
)
}
}
fn store_stack(mem: impl Into<SyntheticAmode>, from_reg: Reg, ty: Type) -> Inst {
let (is_int, size) = match ty {
types::B1 | types::B8 | types::I8 => (true, 1),
types::B16 | types::I16 => (true, 2),
types::B32 | types::I32 => (true, 4),
types::B64 | types::I64 | types::R64 => (true, 8),
types::F32 => (false, 4),
types::F64 => (false, 8),
_ => unimplemented!("store_stack({})", ty),
};
let mem = mem.into();
if is_int {
Inst::mov_r_m(size, from_reg, mem, /* infallible store */ None)
} else {
let sse_op = match size {
4 => SseOpcode::Movss,
8 => SseOpcode::Movsd,
_ => unreachable!(),
};
Inst::xmm_mov_r_m(sse_op, from_reg, mem, /* infallible store */ None)
}
}
/// X64 ABI object for a function call.
pub struct X64ABICall {
sig: ABISig,
@@ -1212,11 +1157,9 @@ impl ABICall for X64ABICall {
debug_assert!(off <= u32::max_value() as i64);
debug_assert!(off >= 0);
ctx.emit(store_stack(
Amode::imm_reg(off as u32, regs::rsp()),
from_reg,
ty,
))
let to_mem = Amode::imm_reg(off as u32, regs::rsp());
let store = Inst::store(ty, from_reg, to_mem, /* infallible store */ None);
ctx.emit(store)
}
}
}
@@ -1225,21 +1168,25 @@ impl ABICall for X64ABICall {
&self,
ctx: &mut C,
idx: usize,
into_reg: Writable<Reg>,
to_reg: Writable<Reg>,
) {
match &self.sig.rets[idx] {
&ABIArg::Reg(reg, ty, _) => ctx.emit(Inst::gen_move(into_reg, reg.to_reg(), ty)),
&ABIArg::Reg(reg, ty, _) => ctx.emit(Inst::gen_move(to_reg, reg.to_reg(), ty)),
&ABIArg::Stack(off, ty, _) => {
let ret_area_base = self.sig.stack_arg_space;
let sp_offset = off + ret_area_base;
// TODO handle offsets bigger than u32::max
debug_assert!(sp_offset >= 0);
debug_assert!(sp_offset <= u32::max_value() as i64);
ctx.emit(load_stack(
Amode::imm_reg(sp_offset as u32, regs::rsp()),
into_reg,
let from_addr = Amode::imm_reg(sp_offset as u32, regs::rsp());
let load = Inst::load(
ty,
));
from_addr,
to_reg,
ExtKind::ZeroExtend,
/* infallible load */ None,
);
ctx.emit(load);
}
}
}

View File

@@ -380,6 +380,8 @@ pub enum SseOpcode {
Movaps,
Movapd,
Movd,
Movdqa,
Movdqu,
Movq,
Movss,
Movsd,
@@ -395,6 +397,9 @@ pub enum SseOpcode {
Paddd,
Paddq,
Paddw,
Pmulld,
Pmullw,
Pmuludq,
Psllw,
Pslld,
Psllq,
@@ -484,6 +489,8 @@ impl SseOpcode {
| SseOpcode::Movq
| SseOpcode::Movsd
| SseOpcode::Movupd
| SseOpcode::Movdqa
| SseOpcode::Movdqu
| SseOpcode::Mulpd
| SseOpcode::Mulsd
| SseOpcode::Orpd
@@ -491,6 +498,8 @@ impl SseOpcode {
| SseOpcode::Paddd
| SseOpcode::Paddq
| SseOpcode::Paddw
| SseOpcode::Pmullw
| SseOpcode::Pmuludq
| SseOpcode::Psllw
| SseOpcode::Pslld
| SseOpcode::Psllq
@@ -510,7 +519,9 @@ impl SseOpcode {
| SseOpcode::Ucomisd
| SseOpcode::Xorpd => SSE2,
SseOpcode::Insertps | SseOpcode::Roundss | SseOpcode::Roundsd => SSE41,
SseOpcode::Insertps | SseOpcode::Pmulld | SseOpcode::Roundss | SseOpcode::Roundsd => {
SSE41
}
}
}
@@ -564,6 +575,8 @@ impl fmt::Debug for SseOpcode {
SseOpcode::Movaps => "movaps",
SseOpcode::Movapd => "movapd",
SseOpcode::Movd => "movd",
SseOpcode::Movdqa => "movdqa",
SseOpcode::Movdqu => "movdqu",
SseOpcode::Movq => "movq",
SseOpcode::Movss => "movss",
SseOpcode::Movsd => "movsd",
@@ -579,6 +592,9 @@ impl fmt::Debug for SseOpcode {
SseOpcode::Paddd => "paddd",
SseOpcode::Paddq => "paddq",
SseOpcode::Paddw => "paddw",
SseOpcode::Pmulld => "pmulld",
SseOpcode::Pmullw => "pmullw",
SseOpcode::Pmuludq => "pmuludq",
SseOpcode::Psllw => "psllw",
SseOpcode::Pslld => "pslld",
SseOpcode::Psllq => "psllq",
@@ -618,6 +634,16 @@ impl fmt::Display for SseOpcode {
}
}
/// This defines the ways a value can be extended: either signed- or zero-extension, or none for
/// types that are not extended. Contrast with [ExtMode], which defines the widths from and to which
/// values can be extended.
#[derive(Clone, PartialEq)]
pub enum ExtKind {
None,
SignExtend,
ZeroExtend,
}
/// These indicate ways of extending (widening) a value, using the Intel
/// naming: B(yte) = u8, W(ord) = u16, L(ong)word = u32, Q(uad)word = u64
#[derive(Clone, PartialEq)]
@@ -823,7 +849,7 @@ impl CC {
FloatCC::Ordered => CC::NP,
FloatCC::Unordered => CC::P,
// Alias for NE
FloatCC::NotEqual | FloatCC::OrderedNotEqual => CC::NZ,
FloatCC::OrderedNotEqual => CC::NZ,
// Alias for E
FloatCC::UnorderedOrEqual => CC::Z,
// Alias for A
@@ -833,12 +859,14 @@ impl CC {
FloatCC::UnorderedOrLessThan => CC::B,
FloatCC::UnorderedOrLessThanOrEqual => CC::BE,
FloatCC::Equal
| FloatCC::NotEqual
| FloatCC::LessThan
| FloatCC::LessThanOrEqual
| FloatCC::UnorderedOrGreaterThan
| FloatCC::UnorderedOrGreaterThanOrEqual => {
panic!("No single condition code to guarantee ordered. Treat as special case.")
}
| FloatCC::UnorderedOrGreaterThanOrEqual => panic!(
"{:?} can't be lowered to a CC code; treat as special case.",
floatcc
),
}
}
@@ -984,3 +1012,14 @@ impl OperandSize {
}
}
}
/// An x64 memory fence kind.
#[derive(Clone)]
pub enum FenceKind {
/// `mfence` instruction ("Memory Fence")
MFence,
/// `lfence` instruction ("Load Fence")
LFence,
/// `sfence` instruction ("Store Fence")
SFence,
}

View File

@@ -1,9 +1,9 @@
use crate::binemit::Reloc;
use crate::ir::immediates::{Ieee32, Ieee64};
use crate::ir::{types, TrapCode};
use crate::ir::TrapCode;
use crate::isa::x64::inst::args::*;
use crate::isa::x64::inst::*;
use crate::machinst::{MachBuffer, MachInstEmit, MachLabel};
use crate::machinst::{inst_common, MachBuffer, MachInstEmit, MachLabel};
use core::convert::TryInto;
use log::debug;
use regalloc::{Reg, RegClass, Writable};
@@ -118,25 +118,38 @@ impl RexFlags {
}
}
/// For specifying the legacy prefixes (or `None` if no prefix required) to
/// be used at the start an instruction. A given prefix may be required for
/// various operations, including instructions that operate on GPR, SSE, and Vex
/// registers.
enum LegacyPrefix {
/// We may need to include one or more legacy prefix bytes before the REX prefix. This enum
/// covers only the small set of possibilities that we actually need.
enum LegacyPrefixes {
/// No prefix bytes
None,
/// Operand Size Override -- here, denoting "16-bit operation"
_66,
/// The Lock prefix
_F0,
/// Operand size override and Lock
_66F0,
/// REPNE, but no specific meaning here -- is just an opcode extension
_F2,
/// REP/REPE, but no specific meaning here -- is just an opcode extension
_F3,
}
impl LegacyPrefix {
impl LegacyPrefixes {
#[inline(always)]
fn emit(&self, sink: &mut MachBuffer<Inst>) {
match self {
LegacyPrefix::_66 => sink.put1(0x66),
LegacyPrefix::_F2 => sink.put1(0xF2),
LegacyPrefix::_F3 => sink.put1(0xF3),
LegacyPrefix::None => (),
LegacyPrefixes::_66 => sink.put1(0x66),
LegacyPrefixes::_F0 => sink.put1(0xF0),
LegacyPrefixes::_66F0 => {
// I don't think the order matters, but in any case, this is the same order that
// the GNU assembler uses.
sink.put1(0x66);
sink.put1(0xF0);
}
LegacyPrefixes::_F2 => sink.put1(0xF2),
LegacyPrefixes::_F3 => sink.put1(0xF3),
LegacyPrefixes::None => (),
}
}
}
@@ -145,15 +158,16 @@ impl LegacyPrefix {
///
/// For an instruction that has as operands a reg encoding `enc_g` and a memory address `mem_e`,
/// create and emit:
/// - first the REX prefix,
/// - first the legacy prefixes, if any
/// - then the REX prefix, if needed
/// - then caller-supplied opcode byte(s) (`opcodes` and `num_opcodes`),
/// - then the MOD/RM byte,
/// - then optionally, a SIB byte,
/// - and finally optionally an immediate that will be derived from the `mem_e` operand.
///
/// For most instructions up to and including SSE4.2, that will be the whole instruction: this is
/// what we call "standard" instructions, and abbreviate "std" in the name here. VEX instructions
/// will require their own emitter functions.
/// what we call "standard" instructions, and abbreviate "std" in the name here. VEX-prefixed
/// instructions will require their own emitter functions.
///
/// This will also work for 32-bits x86 instructions, assuming no REX prefix is provided.
///
@@ -168,7 +182,7 @@ impl LegacyPrefix {
/// indicate a 64-bit operation.
fn emit_std_enc_mem(
sink: &mut MachBuffer<Inst>,
prefix: LegacyPrefix,
prefixes: LegacyPrefixes,
opcodes: u32,
mut num_opcodes: usize,
enc_g: u8,
@@ -179,7 +193,7 @@ fn emit_std_enc_mem(
// 64-bit integer registers, because they are part of an address
// expression. But `enc_g` can be derived from a register of any class.
prefix.emit(sink);
prefixes.emit(sink);
match mem_e {
Amode::ImmReg { simm32, base } => {
@@ -304,7 +318,7 @@ fn emit_std_enc_mem(
/// operand is a register rather than memory. Hence it is much simpler.
fn emit_std_enc_enc(
sink: &mut MachBuffer<Inst>,
prefix: LegacyPrefix,
prefixes: LegacyPrefixes,
opcodes: u32,
mut num_opcodes: usize,
enc_g: u8,
@@ -316,8 +330,8 @@ fn emit_std_enc_enc(
// integer-to-FP conversion insn, one might be RegClass::I64 and the other
// RegClass::V128.
// The operand-size override.
prefix.emit(sink);
// The legacy prefixes.
prefixes.emit(sink);
// The rex byte.
rex.emit_two_op(sink, enc_g, enc_e);
@@ -338,7 +352,7 @@ fn emit_std_enc_enc(
fn emit_std_reg_mem(
sink: &mut MachBuffer<Inst>,
prefix: LegacyPrefix,
prefixes: LegacyPrefixes,
opcodes: u32,
num_opcodes: usize,
reg_g: Reg,
@@ -346,12 +360,12 @@ fn emit_std_reg_mem(
rex: RexFlags,
) {
let enc_g = reg_enc(reg_g);
emit_std_enc_mem(sink, prefix, opcodes, num_opcodes, enc_g, mem_e, rex);
emit_std_enc_mem(sink, prefixes, opcodes, num_opcodes, enc_g, mem_e, rex);
}
fn emit_std_reg_reg(
sink: &mut MachBuffer<Inst>,
prefix: LegacyPrefix,
prefixes: LegacyPrefixes,
opcodes: u32,
num_opcodes: usize,
reg_g: Reg,
@@ -360,7 +374,7 @@ fn emit_std_reg_reg(
) {
let enc_g = reg_enc(reg_g);
let enc_e = reg_enc(reg_e);
emit_std_enc_enc(sink, prefix, opcodes, num_opcodes, enc_g, enc_e, rex);
emit_std_enc_enc(sink, prefixes, opcodes, num_opcodes, enc_g, enc_e, rex);
}
/// Write a suitable number of bits from an imm64 to the sink.
@@ -481,7 +495,7 @@ pub(crate) fn emit(
RegMemImm::Reg { reg: reg_e } => {
emit_std_reg_reg(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
0x0FAF,
2,
reg_g.to_reg(),
@@ -493,7 +507,7 @@ pub(crate) fn emit(
RegMemImm::Mem { addr } => {
emit_std_reg_mem(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
0x0FAF,
2,
reg_g.to_reg(),
@@ -508,7 +522,7 @@ pub(crate) fn emit(
// Yes, really, reg_g twice.
emit_std_reg_reg(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
opcode,
1,
reg_g.to_reg(),
@@ -535,7 +549,7 @@ pub(crate) fn emit(
// code easily.
emit_std_reg_reg(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
opcode_r,
1,
*reg_e,
@@ -550,7 +564,7 @@ pub(crate) fn emit(
// Here we revert to the "normal" G-E ordering.
emit_std_reg_mem(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
opcode_m,
1,
reg_g.to_reg(),
@@ -566,7 +580,7 @@ pub(crate) fn emit(
let enc_g = int_reg_enc(reg_g.to_reg());
emit_std_enc_enc(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
opcode,
1,
subopcode_i,
@@ -581,9 +595,9 @@ pub(crate) fn emit(
Inst::UnaryRmR { size, op, src, dst } => {
let (prefix, rex_flags) = match size {
2 => (LegacyPrefix::_66, RexFlags::clear_w()),
4 => (LegacyPrefix::None, RexFlags::clear_w()),
8 => (LegacyPrefix::None, RexFlags::set_w()),
2 => (LegacyPrefixes::_66, RexFlags::clear_w()),
4 => (LegacyPrefixes::None, RexFlags::clear_w()),
8 => (LegacyPrefixes::None, RexFlags::set_w()),
_ => unreachable!(),
};
@@ -621,9 +635,9 @@ pub(crate) fn emit(
loc,
} => {
let (prefix, rex_flags) = match size {
2 => (LegacyPrefix::_66, RexFlags::clear_w()),
4 => (LegacyPrefix::None, RexFlags::clear_w()),
8 => (LegacyPrefix::None, RexFlags::set_w()),
2 => (LegacyPrefixes::_66, RexFlags::clear_w()),
4 => (LegacyPrefixes::None, RexFlags::clear_w()),
8 => (LegacyPrefixes::None, RexFlags::set_w()),
_ => unreachable!(),
};
@@ -649,9 +663,9 @@ pub(crate) fn emit(
Inst::MulHi { size, signed, rhs } => {
let (prefix, rex_flags) = match size {
2 => (LegacyPrefix::_66, RexFlags::clear_w()),
4 => (LegacyPrefix::None, RexFlags::clear_w()),
8 => (LegacyPrefix::None, RexFlags::set_w()),
2 => (LegacyPrefixes::_66, RexFlags::clear_w()),
4 => (LegacyPrefixes::None, RexFlags::clear_w()),
8 => (LegacyPrefixes::None, RexFlags::set_w()),
_ => unreachable!(),
};
@@ -826,7 +840,7 @@ pub(crate) fn emit(
} else {
RexFlags::clear_w()
};
emit_std_reg_reg(sink, LegacyPrefix::None, 0x89, 1, *src, dst.to_reg(), rex);
emit_std_reg_reg(sink, LegacyPrefixes::None, 0x89, 1, *src, dst.to_reg(), rex);
}
Inst::MovZX_RM_R {
@@ -880,7 +894,7 @@ pub(crate) fn emit(
}
emit_std_reg_reg(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
opcodes,
num_opcodes,
dst.to_reg(),
@@ -899,7 +913,7 @@ pub(crate) fn emit(
emit_std_reg_mem(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
opcodes,
num_opcodes,
dst.to_reg(),
@@ -920,7 +934,7 @@ pub(crate) fn emit(
emit_std_reg_mem(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
0x8B,
1,
dst.to_reg(),
@@ -931,7 +945,7 @@ pub(crate) fn emit(
Inst::LoadEffectiveAddress { addr, dst } => emit_std_reg_mem(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
0x8D,
1,
dst.to_reg(),
@@ -982,7 +996,7 @@ pub(crate) fn emit(
}
emit_std_reg_reg(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
opcodes,
num_opcodes,
dst.to_reg(),
@@ -1001,7 +1015,7 @@ pub(crate) fn emit(
emit_std_reg_mem(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
opcodes,
num_opcodes,
dst.to_reg(),
@@ -1038,14 +1052,14 @@ pub(crate) fn emit(
};
// MOV r8, r/m8 is (REX.W==0) 88 /r
emit_std_reg_mem(sink, LegacyPrefix::None, 0x88, 1, *src, dst, rex)
emit_std_reg_mem(sink, LegacyPrefixes::None, 0x88, 1, *src, dst, rex)
}
2 => {
// MOV r16, r/m16 is 66 (REX.W==0) 89 /r
emit_std_reg_mem(
sink,
LegacyPrefix::_66,
LegacyPrefixes::_66,
0x89,
1,
*src,
@@ -1058,7 +1072,7 @@ pub(crate) fn emit(
// MOV r32, r/m32 is (REX.W==0) 89 /r
emit_std_reg_mem(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
0x89,
1,
*src,
@@ -1071,7 +1085,7 @@ pub(crate) fn emit(
// MOV r64, r/m64 is (REX.W==1) 89 /r
emit_std_reg_mem(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
0x89,
1,
*src,
@@ -1109,7 +1123,7 @@ pub(crate) fn emit(
None => {
// SHL/SHR/SAR %cl, reg32 is (REX.W==0) D3 /subopcode
// SHL/SHR/SAR %cl, reg64 is (REX.W==1) D3 /subopcode
emit_std_enc_enc(sink, LegacyPrefix::None, 0xD3, 1, subopcode, enc_dst, rex);
emit_std_enc_enc(sink, LegacyPrefixes::None, 0xD3, 1, subopcode, enc_dst, rex);
}
Some(num_bits) => {
@@ -1117,7 +1131,7 @@ pub(crate) fn emit(
// SHL/SHR/SAR $ib, reg64 is (REX.W==1) C1 /subopcode ib
// When the shift amount is 1, there's an even shorter encoding, but we don't
// bother with that nicety here.
emit_std_enc_enc(sink, LegacyPrefix::None, 0xC1, 1, subopcode, enc_dst, rex);
emit_std_enc_enc(sink, LegacyPrefixes::None, 0xC1, 1, subopcode, enc_dst, rex);
sink.put1(*num_bits);
}
}
@@ -1125,7 +1139,7 @@ pub(crate) fn emit(
Inst::XmmRmiReg { opcode, src, dst } => {
let rex = RexFlags::clear_w();
let prefix = LegacyPrefix::_66;
let prefix = LegacyPrefixes::_66;
if let RegMemImm::Imm { simm32 } = src {
let (opcode_bytes, reg_digit) = match opcode {
SseOpcode::Psllw => (0x0F71, 6),
@@ -1175,9 +1189,9 @@ pub(crate) fn emit(
src: src_e,
dst: reg_g,
} => {
let mut prefix = LegacyPrefix::None;
let mut prefix = LegacyPrefixes::None;
if *size == 2 {
prefix = LegacyPrefix::_66;
prefix = LegacyPrefixes::_66;
}
let mut rex = match size {
@@ -1245,7 +1259,7 @@ pub(crate) fn emit(
rex_flags.always_emit();
emit_std_enc_enc(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
opcode,
2,
0,
@@ -1261,9 +1275,9 @@ pub(crate) fn emit(
dst: reg_g,
} => {
let (prefix, rex_flags) = match size {
2 => (LegacyPrefix::_66, RexFlags::clear_w()),
4 => (LegacyPrefix::None, RexFlags::clear_w()),
8 => (LegacyPrefix::None, RexFlags::set_w()),
2 => (LegacyPrefixes::_66, RexFlags::clear_w()),
4 => (LegacyPrefixes::None, RexFlags::clear_w()),
8 => (LegacyPrefixes::None, RexFlags::set_w()),
_ => unreachable!("invalid size spec for cmove"),
};
let opcode = 0x0F40 + cc.get_enc() as u32;
@@ -1284,6 +1298,8 @@ pub(crate) fn emit(
src,
dst,
} => {
// Lowering of the Select IR opcode when the input is an fcmp relies on the fact that
// this doesn't clobber flags. Make sure to not do so here.
let next = sink.get_label();
// Jump if cc is *not* set.
@@ -1315,7 +1331,7 @@ pub(crate) fn emit(
let addr = &addr.finalize(state);
emit_std_enc_mem(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
0xFF,
1,
6, /*subopcode*/
@@ -1371,7 +1387,7 @@ pub(crate) fn emit(
let reg_enc = int_reg_enc(*reg);
emit_std_enc_enc(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
0xFF,
1,
2, /*subopcode*/
@@ -1384,7 +1400,7 @@ pub(crate) fn emit(
let addr = &addr.finalize(state);
emit_std_enc_mem(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
0xFF,
1,
2, /*subopcode*/
@@ -1418,6 +1434,21 @@ pub(crate) fn emit(
sink.put4(disp);
}
Inst::JmpIf { cc, taken } => {
let cond_start = sink.cur_offset();
let cond_disp_off = cond_start + 2;
if let Some(l) = taken.as_label() {
sink.use_label_at_offset(cond_disp_off, l, LabelUse::JmpRel32);
// Since this is not a terminator, don't enroll in the branch inversion mechanism.
}
let taken_disp = taken.as_offset32_or_zero();
let taken_disp = taken_disp as u32;
sink.put1(0x0F);
sink.put1(0x80 + cc.get_enc());
sink.put4(taken_disp);
}
Inst::JmpCond {
cc,
taken,
@@ -1461,7 +1492,7 @@ pub(crate) fn emit(
let reg_enc = int_reg_enc(*reg);
emit_std_enc_enc(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
0xFF,
1,
4, /*subopcode*/
@@ -1474,7 +1505,7 @@ pub(crate) fn emit(
let addr = &addr.finalize(state);
emit_std_enc_mem(
sink,
LegacyPrefix::None,
LegacyPrefixes::None,
0xFF,
1,
4, /*subopcode*/
@@ -1596,18 +1627,20 @@ pub(crate) fn emit(
let rex = RexFlags::clear_w();
let (prefix, opcode) = match op {
SseOpcode::Movaps => (LegacyPrefix::None, 0x0F28),
SseOpcode::Movapd => (LegacyPrefix::_66, 0x0F28),
SseOpcode::Movsd => (LegacyPrefix::_F2, 0x0F10),
SseOpcode::Movss => (LegacyPrefix::_F3, 0x0F10),
SseOpcode::Movups => (LegacyPrefix::None, 0x0F10),
SseOpcode::Movupd => (LegacyPrefix::_66, 0x0F10),
SseOpcode::Sqrtps => (LegacyPrefix::None, 0x0F51),
SseOpcode::Sqrtpd => (LegacyPrefix::_66, 0x0F51),
SseOpcode::Sqrtss => (LegacyPrefix::_F3, 0x0F51),
SseOpcode::Sqrtsd => (LegacyPrefix::_F2, 0x0F51),
SseOpcode::Cvtss2sd => (LegacyPrefix::_F3, 0x0F5A),
SseOpcode::Cvtsd2ss => (LegacyPrefix::_F2, 0x0F5A),
SseOpcode::Cvtss2sd => (LegacyPrefixes::_F3, 0x0F5A),
SseOpcode::Cvtsd2ss => (LegacyPrefixes::_F2, 0x0F5A),
SseOpcode::Movaps => (LegacyPrefixes::None, 0x0F28),
SseOpcode::Movapd => (LegacyPrefixes::_66, 0x0F28),
SseOpcode::Movdqa => (LegacyPrefixes::_66, 0x0F6F),
SseOpcode::Movdqu => (LegacyPrefixes::_F3, 0x0F6F),
SseOpcode::Movsd => (LegacyPrefixes::_F2, 0x0F10),
SseOpcode::Movss => (LegacyPrefixes::_F3, 0x0F10),
SseOpcode::Movups => (LegacyPrefixes::None, 0x0F10),
SseOpcode::Movupd => (LegacyPrefixes::_66, 0x0F10),
SseOpcode::Sqrtps => (LegacyPrefixes::None, 0x0F51),
SseOpcode::Sqrtpd => (LegacyPrefixes::_66, 0x0F51),
SseOpcode::Sqrtss => (LegacyPrefixes::_F3, 0x0F51),
SseOpcode::Sqrtsd => (LegacyPrefixes::_F2, 0x0F51),
_ => unimplemented!("Opcode {:?} not implemented", op),
};
@@ -1632,57 +1665,60 @@ pub(crate) fn emit(
dst: reg_g,
} => {
let rex = RexFlags::clear_w();
let (prefix, opcode) = match op {
SseOpcode::Addps => (LegacyPrefix::None, 0x0F58),
SseOpcode::Addpd => (LegacyPrefix::_66, 0x0F58),
SseOpcode::Addss => (LegacyPrefix::_F3, 0x0F58),
SseOpcode::Addsd => (LegacyPrefix::_F2, 0x0F58),
SseOpcode::Andpd => (LegacyPrefix::_66, 0x0F54),
SseOpcode::Andps => (LegacyPrefix::None, 0x0F54),
SseOpcode::Andnps => (LegacyPrefix::None, 0x0F55),
SseOpcode::Andnpd => (LegacyPrefix::_66, 0x0F55),
SseOpcode::Divps => (LegacyPrefix::None, 0x0F5E),
SseOpcode::Divpd => (LegacyPrefix::_66, 0x0F5E),
SseOpcode::Divss => (LegacyPrefix::_F3, 0x0F5E),
SseOpcode::Divsd => (LegacyPrefix::_F2, 0x0F5E),
SseOpcode::Minps => (LegacyPrefix::None, 0x0F5D),
SseOpcode::Minpd => (LegacyPrefix::_66, 0x0F5D),
SseOpcode::Minss => (LegacyPrefix::_F3, 0x0F5D),
SseOpcode::Minsd => (LegacyPrefix::_F2, 0x0F5D),
SseOpcode::Maxps => (LegacyPrefix::None, 0x0F5F),
SseOpcode::Maxpd => (LegacyPrefix::_66, 0x0F5F),
SseOpcode::Maxss => (LegacyPrefix::_F3, 0x0F5F),
SseOpcode::Maxsd => (LegacyPrefix::_F2, 0x0F5F),
SseOpcode::Mulps => (LegacyPrefix::None, 0x0F59),
SseOpcode::Mulpd => (LegacyPrefix::_66, 0x0F59),
SseOpcode::Mulss => (LegacyPrefix::_F3, 0x0F59),
SseOpcode::Mulsd => (LegacyPrefix::_F2, 0x0F59),
SseOpcode::Orpd => (LegacyPrefix::_66, 0x0F56),
SseOpcode::Orps => (LegacyPrefix::None, 0x0F56),
SseOpcode::Paddb => (LegacyPrefix::_66, 0x0FFC),
SseOpcode::Paddd => (LegacyPrefix::_66, 0x0FFE),
SseOpcode::Paddq => (LegacyPrefix::_66, 0x0FD4),
SseOpcode::Paddw => (LegacyPrefix::_66, 0x0FFD),
SseOpcode::Psubb => (LegacyPrefix::_66, 0x0FF8),
SseOpcode::Psubd => (LegacyPrefix::_66, 0x0FFA),
SseOpcode::Psubq => (LegacyPrefix::_66, 0x0FFB),
SseOpcode::Psubw => (LegacyPrefix::_66, 0x0FF9),
SseOpcode::Subps => (LegacyPrefix::None, 0x0F5C),
SseOpcode::Subpd => (LegacyPrefix::_66, 0x0F5C),
SseOpcode::Subss => (LegacyPrefix::_F3, 0x0F5C),
SseOpcode::Subsd => (LegacyPrefix::_F2, 0x0F5C),
SseOpcode::Xorps => (LegacyPrefix::None, 0x0F57),
SseOpcode::Xorpd => (LegacyPrefix::_66, 0x0F57),
let (prefix, opcode, length) = match op {
SseOpcode::Addps => (LegacyPrefixes::None, 0x0F58, 2),
SseOpcode::Addpd => (LegacyPrefixes::_66, 0x0F58, 2),
SseOpcode::Addss => (LegacyPrefixes::_F3, 0x0F58, 2),
SseOpcode::Addsd => (LegacyPrefixes::_F2, 0x0F58, 2),
SseOpcode::Andpd => (LegacyPrefixes::_66, 0x0F54, 2),
SseOpcode::Andps => (LegacyPrefixes::None, 0x0F54, 2),
SseOpcode::Andnps => (LegacyPrefixes::None, 0x0F55, 2),
SseOpcode::Andnpd => (LegacyPrefixes::_66, 0x0F55, 2),
SseOpcode::Divps => (LegacyPrefixes::None, 0x0F5E, 2),
SseOpcode::Divpd => (LegacyPrefixes::_66, 0x0F5E, 2),
SseOpcode::Divss => (LegacyPrefixes::_F3, 0x0F5E, 2),
SseOpcode::Divsd => (LegacyPrefixes::_F2, 0x0F5E, 2),
SseOpcode::Minps => (LegacyPrefixes::None, 0x0F5D, 2),
SseOpcode::Minpd => (LegacyPrefixes::_66, 0x0F5D, 2),
SseOpcode::Minss => (LegacyPrefixes::_F3, 0x0F5D, 2),
SseOpcode::Minsd => (LegacyPrefixes::_F2, 0x0F5D, 2),
SseOpcode::Maxps => (LegacyPrefixes::None, 0x0F5F, 2),
SseOpcode::Maxpd => (LegacyPrefixes::_66, 0x0F5F, 2),
SseOpcode::Maxss => (LegacyPrefixes::_F3, 0x0F5F, 2),
SseOpcode::Maxsd => (LegacyPrefixes::_F2, 0x0F5F, 2),
SseOpcode::Mulps => (LegacyPrefixes::None, 0x0F59, 2),
SseOpcode::Mulpd => (LegacyPrefixes::_66, 0x0F59, 2),
SseOpcode::Mulss => (LegacyPrefixes::_F3, 0x0F59, 2),
SseOpcode::Mulsd => (LegacyPrefixes::_F2, 0x0F59, 2),
SseOpcode::Orpd => (LegacyPrefixes::_66, 0x0F56, 2),
SseOpcode::Orps => (LegacyPrefixes::None, 0x0F56, 2),
SseOpcode::Paddb => (LegacyPrefixes::_66, 0x0FFC, 2),
SseOpcode::Paddd => (LegacyPrefixes::_66, 0x0FFE, 2),
SseOpcode::Paddq => (LegacyPrefixes::_66, 0x0FD4, 2),
SseOpcode::Paddw => (LegacyPrefixes::_66, 0x0FFD, 2),
SseOpcode::Pmulld => (LegacyPrefixes::_66, 0x0F3840, 3),
SseOpcode::Pmullw => (LegacyPrefixes::_66, 0x0FD5, 2),
SseOpcode::Pmuludq => (LegacyPrefixes::_66, 0x0FF4, 2),
SseOpcode::Psubb => (LegacyPrefixes::_66, 0x0FF8, 2),
SseOpcode::Psubd => (LegacyPrefixes::_66, 0x0FFA, 2),
SseOpcode::Psubq => (LegacyPrefixes::_66, 0x0FFB, 2),
SseOpcode::Psubw => (LegacyPrefixes::_66, 0x0FF9, 2),
SseOpcode::Subps => (LegacyPrefixes::None, 0x0F5C, 2),
SseOpcode::Subpd => (LegacyPrefixes::_66, 0x0F5C, 2),
SseOpcode::Subss => (LegacyPrefixes::_F3, 0x0F5C, 2),
SseOpcode::Subsd => (LegacyPrefixes::_F2, 0x0F5C, 2),
SseOpcode::Xorps => (LegacyPrefixes::None, 0x0F57, 2),
SseOpcode::Xorpd => (LegacyPrefixes::_66, 0x0F57, 2),
_ => unimplemented!("Opcode {:?} not implemented", op),
};
match src_e {
RegMem::Reg { reg: reg_e } => {
emit_std_reg_reg(sink, prefix, opcode, 2, reg_g.to_reg(), *reg_e, rex);
emit_std_reg_reg(sink, prefix, opcode, length, reg_g.to_reg(), *reg_e, rex);
}
RegMem::Mem { addr } => {
let addr = &addr.finalize(state);
emit_std_reg_mem(sink, prefix, opcode, 2, reg_g.to_reg(), addr, rex);
emit_std_reg_mem(sink, prefix, opcode, length, reg_g.to_reg(), addr, rex);
}
}
}
@@ -1775,10 +1811,10 @@ pub(crate) fn emit(
Inst::XmmRmRImm { op, src, dst, imm } => {
let prefix = match op {
SseOpcode::Cmpps => LegacyPrefix::None,
SseOpcode::Cmppd => LegacyPrefix::_66,
SseOpcode::Cmpss => LegacyPrefix::_F3,
SseOpcode::Cmpsd => LegacyPrefix::_F2,
SseOpcode::Cmpps => LegacyPrefixes::None,
SseOpcode::Cmppd => LegacyPrefixes::_66,
SseOpcode::Cmpss => LegacyPrefixes::_F3,
SseOpcode::Cmpsd => LegacyPrefixes::_F2,
_ => unimplemented!("Opcode {:?} not implemented", op),
};
let opcode = 0x0FC2;
@@ -1802,17 +1838,9 @@ pub(crate) fn emit(
// "constant inline" code should be replaced by constant pool integration.
// Load the inline constant.
let opcode = match *ty {
types::F32X4 => SseOpcode::Movups,
types::F64X2 => SseOpcode::Movupd,
types::I8X16 => SseOpcode::Movupd, // TODO replace with MOVDQU
_ => unimplemented!("cannot yet load constants for type: {}", ty),
};
let constant_start_label = sink.get_label();
let load_offset = RegMem::mem(Amode::rip_relative(BranchTarget::Label(
constant_start_label,
)));
let load = Inst::xmm_unary_rm_r(opcode, load_offset, *dst);
let load_offset = Amode::rip_relative(BranchTarget::Label(constant_start_label));
let load = Inst::load(*ty, load_offset, *dst, ExtKind::None, None);
load.emit(sink, flags, state);
// Jump over the constant.
@@ -1836,10 +1864,14 @@ pub(crate) fn emit(
srcloc,
} => {
let (prefix, opcode) = match op {
SseOpcode::Movss => (LegacyPrefix::_F3, 0x0F11),
SseOpcode::Movsd => (LegacyPrefix::_F2, 0x0F11),
SseOpcode::Movaps => (LegacyPrefix::None, 0x0F29),
SseOpcode::Movups => (LegacyPrefix::None, 0x0F11),
SseOpcode::Movaps => (LegacyPrefixes::None, 0x0F29),
SseOpcode::Movapd => (LegacyPrefixes::_66, 0x0F29),
SseOpcode::Movdqa => (LegacyPrefixes::_66, 0x0F7F),
SseOpcode::Movdqu => (LegacyPrefixes::_F3, 0x0F7F),
SseOpcode::Movss => (LegacyPrefixes::_F3, 0x0F11),
SseOpcode::Movsd => (LegacyPrefixes::_F2, 0x0F11),
SseOpcode::Movups => (LegacyPrefixes::None, 0x0F11),
SseOpcode::Movupd => (LegacyPrefixes::_66, 0x0F11),
_ => unimplemented!("Opcode {:?} not implemented", op),
};
let dst = &dst.finalize(state);
@@ -1859,9 +1891,9 @@ pub(crate) fn emit(
let (prefix, opcode, dst_first) = match op {
// Movd and movq use the same opcode; the presence of the REX prefix (set below)
// actually determines which is used.
SseOpcode::Movd | SseOpcode::Movq => (LegacyPrefix::_66, 0x0F7E, false),
SseOpcode::Cvttss2si => (LegacyPrefix::_F3, 0x0F2C, true),
SseOpcode::Cvttsd2si => (LegacyPrefix::_F2, 0x0F2C, true),
SseOpcode::Movd | SseOpcode::Movq => (LegacyPrefixes::_66, 0x0F7E, false),
SseOpcode::Cvttss2si => (LegacyPrefixes::_F3, 0x0F2C, true),
SseOpcode::Cvttsd2si => (LegacyPrefixes::_F2, 0x0F2C, true),
_ => panic!("unexpected opcode {:?}", op),
};
let rex = match dst_size {
@@ -1887,9 +1919,9 @@ pub(crate) fn emit(
let (prefix, opcode) = match op {
// Movd and movq use the same opcode; the presence of the REX prefix (set below)
// actually determines which is used.
SseOpcode::Movd | SseOpcode::Movq => (LegacyPrefix::_66, 0x0F6E),
SseOpcode::Cvtsi2ss => (LegacyPrefix::_F3, 0x0F2A),
SseOpcode::Cvtsi2sd => (LegacyPrefix::_F2, 0x0F2A),
SseOpcode::Movd | SseOpcode::Movq => (LegacyPrefixes::_66, 0x0F6E),
SseOpcode::Cvtsi2ss => (LegacyPrefixes::_F3, 0x0F2A),
SseOpcode::Cvtsi2sd => (LegacyPrefixes::_F2, 0x0F2A),
_ => panic!("unexpected opcode {:?}", op),
};
let rex = match *src_size {
@@ -1910,8 +1942,8 @@ pub(crate) fn emit(
Inst::XMM_Cmp_RM_R { op, src, dst } => {
let rex = RexFlags::clear_w();
let (prefix, opcode) = match op {
SseOpcode::Ucomisd => (LegacyPrefix::_66, 0x0F2E),
SseOpcode::Ucomiss => (LegacyPrefix::None, 0x0F2E),
SseOpcode::Ucomisd => (LegacyPrefixes::_66, 0x0F2E),
SseOpcode::Ucomiss => (LegacyPrefixes::None, 0x0F2E),
_ => unimplemented!("Emit xmm cmp rm r"),
};
@@ -2430,6 +2462,113 @@ pub(crate) fn emit(
}
}
Inst::LockCmpxchg {
ty,
src,
dst,
srcloc,
} => {
if let Some(srcloc) = srcloc {
sink.add_trap(*srcloc, TrapCode::HeapOutOfBounds);
}
// lock cmpxchg{b,w,l,q} %src, (dst)
// Note that 0xF0 is the Lock prefix.
let (prefix, rex, opcodes) = match *ty {
types::I8 => {
let mut rex_flags = RexFlags::clear_w();
let enc_src = int_reg_enc(*src);
if enc_src >= 4 && enc_src <= 7 {
rex_flags.always_emit();
};
(LegacyPrefixes::_F0, rex_flags, 0x0FB0)
}
types::I16 => (LegacyPrefixes::_66F0, RexFlags::clear_w(), 0x0FB1),
types::I32 => (LegacyPrefixes::_F0, RexFlags::clear_w(), 0x0FB1),
types::I64 => (LegacyPrefixes::_F0, RexFlags::set_w(), 0x0FB1),
_ => unreachable!(),
};
emit_std_reg_mem(sink, prefix, opcodes, 2, *src, &dst.finalize(state), rex);
}
Inst::AtomicRmwSeq { ty, op, srcloc } => {
// Emit this:
//
// mov{zbq,zwq,zlq,q} (%r9), %rax // rax = old value
// again:
// movq %rax, %r11 // rax = old value, r11 = old value
// `op`q %r10, %r11 // rax = old value, r11 = new value
// lock cmpxchg{b,w,l,q} %r11, (%r9) // try to store new value
// jnz again // If this is taken, rax will have a "revised" old value
//
// Operand conventions:
// IN: %r9 (addr), %r10 (2nd arg for `op`)
// OUT: %rax (old value), %r11 (trashed), %rflags (trashed)
//
// In the case where the operation is 'xchg', the "`op`q" instruction is instead
// movq %r10, %r11
// so that we simply write in the destination, the "2nd arg for `op`".
let rax = regs::rax();
let r9 = regs::r9();
let r10 = regs::r10();
let r11 = regs::r11();
let rax_w = Writable::from_reg(rax);
let r11_w = Writable::from_reg(r11);
let amode = Amode::imm_reg(0, r9);
let again_label = sink.get_label();
// mov{zbq,zwq,zlq,q} (%r9), %rax
// No need to call `add_trap` here, since the `i1` emit will do that.
let i1 = Inst::load(*ty, amode.clone(), rax_w, ExtKind::ZeroExtend, *srcloc);
i1.emit(sink, flags, state);
// again:
sink.bind_label(again_label);
// movq %rax, %r11
let i2 = Inst::mov_r_r(true, rax, r11_w);
i2.emit(sink, flags, state);
// opq %r10, %r11
let r10_rmi = RegMemImm::reg(r10);
let i3 = if *op == inst_common::AtomicRmwOp::Xchg {
Inst::mov_r_r(true, r10, r11_w)
} else {
let alu_op = match op {
inst_common::AtomicRmwOp::Add => AluRmiROpcode::Add,
inst_common::AtomicRmwOp::Sub => AluRmiROpcode::Sub,
inst_common::AtomicRmwOp::And => AluRmiROpcode::And,
inst_common::AtomicRmwOp::Or => AluRmiROpcode::Or,
inst_common::AtomicRmwOp::Xor => AluRmiROpcode::Xor,
inst_common::AtomicRmwOp::Xchg => unreachable!(),
};
Inst::alu_rmi_r(true, alu_op, r10_rmi, r11_w)
};
i3.emit(sink, flags, state);
// lock cmpxchg{b,w,l,q} %r11, (%r9)
// No need to call `add_trap` here, since the `i4` emit will do that.
let i4 = Inst::LockCmpxchg {
ty: *ty,
src: r11,
dst: amode.into(),
srcloc: *srcloc,
};
i4.emit(sink, flags, state);
// jnz again
one_way_jmp(sink, CC::NZ, again_label);
}
Inst::Fence { kind } => {
sink.put1(0x0F);
sink.put1(0xAE);
match kind {
FenceKind::MFence => sink.put1(0xF0), // mfence = 0F AE F0
FenceKind::LFence => sink.put1(0xE8), // lfence = 0F AE E8
FenceKind::SFence => sink.put1(0xF8), // sfence = 0F AE F8
}
}
Inst::Hlt => {
sink.put1(0xcc);
}

View File

@@ -4,10 +4,13 @@
//!
//! to see stdout: cargo test -- --nocapture
//!
//! for this specific case:
//! for this specific case, as of 24 Aug 2020:
//!
//! (cd cranelift/codegen && \
//! RUST_BACKTRACE=1 cargo test isa::x64::inst::test_x64_insn_encoding_and_printing -- --nocapture)
//! cd to the top of your wasmtime tree, then:
//! RUST_BACKTRACE=1 cargo test --features test-programs/test_programs \
//! --features experimental_x64 --all --exclude peepmatic --exclude lightbeam \
//! --exclude wasmtime-lightbeam --exclude peepmatic-automata --exclude peepmatic-fuzzing \
//! --exclude peepmatic-macro -- isa::x64::inst::emit_tests::test_x64_emit
use super::*;
use crate::isa::test_utils;
@@ -3062,6 +3065,24 @@ fn test_x64_emit() {
"psubq %xmm8, %xmm1",
));
insns.push((
Inst::xmm_rm_r(SseOpcode::Pmulld, RegMem::reg(xmm15), w_xmm6),
"66410F3840F7",
"pmulld %xmm15, %xmm6",
));
insns.push((
Inst::xmm_rm_r(SseOpcode::Pmullw, RegMem::reg(xmm14), w_xmm1),
"66410FD5CE",
"pmullw %xmm14, %xmm1",
));
insns.push((
Inst::xmm_rm_r(SseOpcode::Pmuludq, RegMem::reg(xmm8), w_xmm9),
"66450FF4C8",
"pmuludq %xmm8, %xmm9",
));
// XMM_Mov_R_M: float stores
insns.push((
Inst::xmm_mov_r_m(SseOpcode::Movss, xmm15, Amode::imm_reg(128, r12), None),
@@ -3254,6 +3275,174 @@ fn test_x64_emit() {
"cmpps $0, %xmm15, %xmm7",
));
// ========================================================
// Pertaining to atomics.
let am1: SyntheticAmode = Amode::imm_reg_reg_shift(321, r10, rdx, 2).into();
// `am2` doesn't contribute any 1 bits to the rex prefix, so we must use it when testing
// for retention of the apparently-redundant rex prefix in the 8-bit case.
let am2: SyntheticAmode = Amode::imm_reg_reg_shift(-12345i32 as u32, rcx, rsi, 3).into();
// A general 8-bit case.
insns.push((
Inst::LockCmpxchg {
ty: types::I8,
src: rbx,
dst: am1,
srcloc: None,
},
"F0410FB09C9241010000",
"lock cmpxchgb %bl, 321(%r10,%rdx,4)",
));
// Check redundant rex retention in 8-bit cases.
insns.push((
Inst::LockCmpxchg {
ty: types::I8,
src: rdx,
dst: am2.clone(),
srcloc: None,
},
"F00FB094F1C7CFFFFF",
"lock cmpxchgb %dl, -12345(%rcx,%rsi,8)",
));
insns.push((
Inst::LockCmpxchg {
ty: types::I8,
src: rsi,
dst: am2.clone(),
srcloc: None,
},
"F0400FB0B4F1C7CFFFFF",
"lock cmpxchgb %sil, -12345(%rcx,%rsi,8)",
));
insns.push((
Inst::LockCmpxchg {
ty: types::I8,
src: r10,
dst: am2.clone(),
srcloc: None,
},
"F0440FB094F1C7CFFFFF",
"lock cmpxchgb %r10b, -12345(%rcx,%rsi,8)",
));
insns.push((
Inst::LockCmpxchg {
ty: types::I8,
src: r15,
dst: am2.clone(),
srcloc: None,
},
"F0440FB0BCF1C7CFFFFF",
"lock cmpxchgb %r15b, -12345(%rcx,%rsi,8)",
));
// 16 bit cases
insns.push((
Inst::LockCmpxchg {
ty: types::I16,
src: rsi,
dst: am2.clone(),
srcloc: None,
},
"66F00FB1B4F1C7CFFFFF",
"lock cmpxchgw %si, -12345(%rcx,%rsi,8)",
));
insns.push((
Inst::LockCmpxchg {
ty: types::I16,
src: r10,
dst: am2.clone(),
srcloc: None,
},
"66F0440FB194F1C7CFFFFF",
"lock cmpxchgw %r10w, -12345(%rcx,%rsi,8)",
));
// 32 bit cases
insns.push((
Inst::LockCmpxchg {
ty: types::I32,
src: rsi,
dst: am2.clone(),
srcloc: None,
},
"F00FB1B4F1C7CFFFFF",
"lock cmpxchgl %esi, -12345(%rcx,%rsi,8)",
));
insns.push((
Inst::LockCmpxchg {
ty: types::I32,
src: r10,
dst: am2.clone(),
srcloc: None,
},
"F0440FB194F1C7CFFFFF",
"lock cmpxchgl %r10d, -12345(%rcx,%rsi,8)",
));
// 64 bit cases
insns.push((
Inst::LockCmpxchg {
ty: types::I64,
src: rsi,
dst: am2.clone(),
srcloc: None,
},
"F0480FB1B4F1C7CFFFFF",
"lock cmpxchgq %rsi, -12345(%rcx,%rsi,8)",
));
insns.push((
Inst::LockCmpxchg {
ty: types::I64,
src: r10,
dst: am2.clone(),
srcloc: None,
},
"F04C0FB194F1C7CFFFFF",
"lock cmpxchgq %r10, -12345(%rcx,%rsi,8)",
));
// AtomicRmwSeq
insns.push((
Inst::AtomicRmwSeq { ty: types::I8, op: inst_common::AtomicRmwOp::Or, srcloc: None },
"490FB6014989C34D09D3F0450FB0190F85EFFFFFFF",
"atomically { 8_bits_at_[%r9]) Or= %r10; %rax = old_value_at_[%r9]; %r11, %rflags = trash }"
));
insns.push((
Inst::AtomicRmwSeq { ty: types::I16, op: inst_common::AtomicRmwOp::And, srcloc: None },
"490FB7014989C34D21D366F0450FB1190F85EEFFFFFF",
"atomically { 16_bits_at_[%r9]) And= %r10; %rax = old_value_at_[%r9]; %r11, %rflags = trash }"
));
insns.push((
Inst::AtomicRmwSeq { ty: types::I32, op: inst_common::AtomicRmwOp::Xchg, srcloc: None },
"418B014989C34D89D3F0450FB1190F85EFFFFFFF",
"atomically { 32_bits_at_[%r9]) Xchg= %r10; %rax = old_value_at_[%r9]; %r11, %rflags = trash }"
));
insns.push((
Inst::AtomicRmwSeq { ty: types::I64, op: inst_common::AtomicRmwOp::Add, srcloc: None },
"498B014989C34D01D3F04D0FB1190F85EFFFFFFF",
"atomically { 64_bits_at_[%r9]) Add= %r10; %rax = old_value_at_[%r9]; %r11, %rflags = trash }"
));
// Fence
insns.push((
Inst::Fence {
kind: FenceKind::MFence,
},
"0FAEF0",
"mfence",
));
insns.push((
Inst::Fence {
kind: FenceKind::LFence,
},
"0FAEE8",
"lfence",
));
insns.push((
Inst::Fence {
kind: FenceKind::SFence,
},
"0FAEF8",
"sfence",
));
// ========================================================
// Misc instructions.

View File

@@ -359,6 +359,16 @@ pub enum Inst {
/// Jump to a known target: jmp simm32.
JmpKnown { dst: BranchTarget },
/// One-way conditional branch: jcond cond target.
///
/// This instruction is useful when we have conditional jumps depending on more than two
/// conditions, see for instance the lowering of Brz/brnz with Fcmp inputs.
///
/// A note of caution: in contexts where the branch target is another block, this has to be the
/// same successor as the one specified in the terminator branch of the current block.
/// Otherwise, this might confuse register allocation by creating new invisible edges.
JmpIf { cc: CC, taken: BranchTarget },
/// Two-way conditional branch: jcond cond target target.
/// Emitted as a compound sequence; the MachBuffer will shrink it as appropriate.
JmpCond {
@@ -404,6 +414,56 @@ pub enum Inst {
offset: i64,
},
// =====================================
// Instructions pertaining to atomic memory accesses.
/// A standard (native) `lock cmpxchg src, (amode)`, with register conventions:
///
/// `dst` (read) address
/// `src` (read) replacement value
/// %rax (modified) in: expected value, out: value that was actually at `dst`
/// %rflags is written. Do not assume anything about it after the instruction.
///
/// The instruction "succeeded" iff the lowest `ty` bits of %rax afterwards are the same as
/// they were before.
LockCmpxchg {
ty: Type, // I8, I16, I32 or I64
src: Reg,
dst: SyntheticAmode,
srcloc: Option<SourceLoc>,
},
/// A synthetic instruction, based on a loop around a native `lock cmpxchg` instruction.
/// This atomically modifies a value in memory and returns the old value. The sequence
/// consists of an initial "normal" load from `dst`, followed by a loop which computes the
/// new value and tries to compare-and-swap ("CAS") it into `dst`, using the native
/// instruction `lock cmpxchg{b,w,l,q}` . The loop iterates until the CAS is successful.
/// If there is no contention, there will be only one pass through the loop body. The
/// sequence does *not* perform any explicit memory fence instructions
/// (mfence/sfence/lfence).
///
/// Note that the transaction is atomic in the sense that, as observed by some other thread,
/// `dst` either has the initial or final value, but no other. It isn't atomic in the sense
/// of guaranteeing that no other thread writes to `dst` in between the initial load and the
/// CAS -- but that would cause the CAS to fail unless the other thread's last write before
/// the CAS wrote the same value that was already there. In other words, this
/// implementation suffers (unavoidably) from the A-B-A problem.
///
/// This instruction sequence has fixed register uses as follows:
///
/// %r9 (read) address
/// %r10 (read) second operand for `op`
/// %r11 (written) scratch reg; value afterwards has no meaning
/// %rax (written) the old value at %r9
/// %rflags is written. Do not assume anything about it after the instruction.
AtomicRmwSeq {
ty: Type, // I8, I16, I32 or I64
op: inst_common::AtomicRmwOp,
srcloc: Option<SourceLoc>,
},
/// A memory fence (mfence, lfence or sfence).
Fence { kind: FenceKind },
// =====================================
// Meta-instructions generating no code.
/// Marker, no-op in generated code: SP "virtual offset" is adjusted. This
@@ -526,6 +586,7 @@ impl Inst {
Inst::Mov_R_R { is_64, src, dst }
}
// TODO Can be replaced by `Inst::move` (high-level) and `Inst::unary_rm_r` (low-level)
pub(crate) fn xmm_mov(
op: SseOpcode,
src: RegMem,
@@ -915,6 +976,10 @@ impl Inst {
Inst::JmpKnown { dst }
}
pub(crate) fn jmp_if(cc: CC, taken: BranchTarget) -> Inst {
Inst::JmpIf { cc, taken }
}
pub(crate) fn jmp_cond(cc: CC, taken: BranchTarget, not_taken: BranchTarget) -> Inst {
Inst::JmpCond {
cc,
@@ -935,6 +1000,85 @@ impl Inst {
srcloc,
}
}
/// Choose which instruction to use for loading a register value from memory. For loads smaller
/// than 64 bits, this method expects a way to extend the value (i.e. [ExtKind::SignExtend],
/// [ExtKind::ZeroExtend]); loads with no extension necessary will ignore this.
pub(crate) fn load(
ty: Type,
from_addr: impl Into<SyntheticAmode>,
to_reg: Writable<Reg>,
ext_kind: ExtKind,
srcloc: Option<SourceLoc>,
) -> Inst {
let rc = to_reg.to_reg().get_class();
match rc {
RegClass::I64 => {
let ext_mode = match ty.bytes() {
1 => Some(ExtMode::BQ),
2 => Some(ExtMode::WQ),
4 => Some(ExtMode::LQ),
8 => None,
_ => unreachable!("the type should never use a scalar load: {}", ty),
};
if let Some(ext_mode) = ext_mode {
// Values smaller than 64 bits must be extended in some way.
match ext_kind {
ExtKind::SignExtend => {
Inst::movsx_rm_r(ext_mode, RegMem::mem(from_addr), to_reg, srcloc)
}
ExtKind::ZeroExtend => {
Inst::movzx_rm_r(ext_mode, RegMem::mem(from_addr), to_reg, srcloc)
}
ExtKind::None => panic!(
"expected an extension kind for extension mode: {:?}",
ext_mode
),
}
} else {
// 64-bit values can be moved directly.
Inst::mov64_m_r(from_addr, to_reg, srcloc)
}
}
RegClass::V128 => {
let opcode = match ty {
types::F32 => SseOpcode::Movss,
types::F64 => SseOpcode::Movsd,
types::F32X4 => SseOpcode::Movups,
types::F64X2 => SseOpcode::Movupd,
_ if ty.is_vector() && ty.bits() == 128 => SseOpcode::Movdqu,
_ => unimplemented!("unable to load type: {}", ty),
};
Inst::xmm_unary_rm_r(opcode, RegMem::mem(from_addr), to_reg)
}
_ => panic!("unable to generate load for register class: {:?}", rc),
}
}
/// Choose which instruction to use for storing a register value to memory.
pub(crate) fn store(
ty: Type,
from_reg: Reg,
to_addr: impl Into<SyntheticAmode>,
srcloc: Option<SourceLoc>,
) -> Inst {
let rc = from_reg.get_class();
match rc {
RegClass::I64 => Inst::mov_r_m(ty.bytes() as u8, from_reg, to_addr, srcloc),
RegClass::V128 => {
let opcode = match ty {
types::F32 => SseOpcode::Movss,
types::F64 => SseOpcode::Movsd,
types::F32X4 => SseOpcode::Movups,
types::F64X2 => SseOpcode::Movupd,
_ if ty.is_vector() && ty.bits() == 128 => SseOpcode::Movdqu,
_ => unimplemented!("unable to store type: {}", ty),
};
Inst::xmm_mov_r_m(opcode, from_reg, to_addr, srcloc)
}
_ => panic!("unable to generate store for register class: {:?}", rc),
}
}
}
// Inst helpers.
@@ -1406,12 +1550,18 @@ impl ShowWithRRU for Inst {
format!("{} {}", ljustify("jmp".to_string()), dst.show_rru(mb_rru))
}
Inst::JmpIf { cc, taken } => format!(
"{} {}",
ljustify2("j".to_string(), cc.to_string()),
taken.show_rru(mb_rru),
),
Inst::JmpCond {
cc,
taken,
not_taken,
} => format!(
"{} taken={} not_taken={}",
"{} {}; j {}",
ljustify2("j".to_string(), cc.to_string()),
taken.show_rru(mb_rru),
not_taken.show_rru(mb_rru)
@@ -1441,6 +1591,26 @@ impl ShowWithRRU for Inst {
show_ireg_sized(dst.to_reg(), mb_rru, 8),
),
Inst::LockCmpxchg { ty, src, dst, .. } => {
let size = ty.bytes() as u8;
format!("lock cmpxchg{} {}, {}",
suffixBWLQ(size), show_ireg_sized(*src, mb_rru, size), dst.show_rru(mb_rru))
}
Inst::AtomicRmwSeq { ty, op, .. } => {
format!(
"atomically {{ {}_bits_at_[%r9]) {:?}= %r10; %rax = old_value_at_[%r9]; %r11, %rflags = trash }}",
ty.bits(), op)
},
Inst::Fence { kind } => {
match kind {
FenceKind::MFence => "mfence".to_string(),
FenceKind::LFence => "lfence".to_string(),
FenceKind::SFence => "sfence".to_string(),
}
}
Inst::VirtualSPOffsetAdj { offset } => format!("virtual_sp_offset_adjust {}", offset),
Inst::Hlt => "hlt".into(),
@@ -1657,15 +1827,30 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
collector.add_def(*dst);
}
Inst::LockCmpxchg { src, dst, .. } => {
dst.get_regs_as_uses(collector);
collector.add_use(*src);
collector.add_mod(Writable::from_reg(regs::rax()));
}
Inst::AtomicRmwSeq { .. } => {
collector.add_use(regs::r9());
collector.add_use(regs::r10());
collector.add_def(Writable::from_reg(regs::r11()));
collector.add_def(Writable::from_reg(regs::rax()));
}
Inst::Ret
| Inst::EpiloguePlaceholder
| Inst::JmpKnown { .. }
| Inst::JmpIf { .. }
| Inst::JmpCond { .. }
| Inst::Nop { .. }
| Inst::TrapIf { .. }
| Inst::VirtualSPOffsetAdj { .. }
| Inst::Hlt
| Inst::Ud2 { .. } => {
| Inst::Ud2 { .. }
| Inst::Fence { .. } => {
// No registers are used.
}
}
@@ -2011,16 +2196,29 @@ fn x64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
Inst::LoadExtName { ref mut dst, .. } => map_def(mapper, dst),
Inst::LockCmpxchg {
ref mut src,
ref mut dst,
..
} => {
map_use(mapper, src);
dst.map_uses(mapper);
}
Inst::Ret
| Inst::EpiloguePlaceholder
| Inst::JmpKnown { .. }
| Inst::JmpCond { .. }
| Inst::JmpIf { .. }
| Inst::Nop { .. }
| Inst::TrapIf { .. }
| Inst::VirtualSPOffsetAdj { .. }
| Inst::Ud2 { .. }
| Inst::Hlt => {
// No registers are used.
| Inst::Hlt
| Inst::AtomicRmwSeq { .. }
| Inst::Fence { .. } => {
// Instruction doesn't explicitly mention any regs, so it can't have any virtual
// regs that we'd need to remap. Hence no action required.
}
}
}
@@ -2093,16 +2291,18 @@ impl MachInst for Inst {
debug_assert!(rc_dst == rc_src);
match rc_dst {
RegClass::I64 => Inst::mov_r_r(true, src_reg, dst_reg),
RegClass::V128 => match ty {
types::F32 => Inst::xmm_mov(SseOpcode::Movss, RegMem::reg(src_reg), dst_reg, None),
types::F64 => Inst::xmm_mov(SseOpcode::Movsd, RegMem::reg(src_reg), dst_reg, None),
_ if ty.is_vector() && ty.bits() == 128 => {
// TODO Specialize this move for different types: MOVUPD, MOVDQU, etc.
Inst::xmm_mov(SseOpcode::Movups, RegMem::reg(src_reg), dst_reg, None)
RegClass::V128 => {
let opcode = match ty {
types::F32 => SseOpcode::Movss,
types::F64 => SseOpcode::Movsd,
types::F32X4 => SseOpcode::Movaps,
types::F64X2 => SseOpcode::Movapd,
_ if ty.is_vector() && ty.bits() == 128 => SseOpcode::Movdqa,
_ => unimplemented!("unable to move type: {}", ty),
};
Inst::xmm_unary_rm_r(opcode, RegMem::reg(src_reg), dst_reg)
}
_ => panic!("unexpected type {:?} in gen_move of regclass V128", ty),
},
_ => panic!("gen_move(x64): unhandled regclass"),
_ => panic!("gen_move(x64): unhandled regclass {:?}", rc_dst),
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
//! A place to park MachInst::Inst fragments which are common across multiple architectures.
use crate::ir;
/// Atomic memory update operations. As of 21 Aug 2020 these are used for the aarch64 and x64
/// targets.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum AtomicRmwOp {
/// Add
Add,
/// Sub
Sub,
/// And
And,
/// Or
Or,
/// Exclusive Or
Xor,
/// Exchange (swap operands)
Xchg,
}
impl AtomicRmwOp {
/// Converts an `ir::AtomicRmwOp` to the corresponding `inst_common::AtomicRmwOp`.
pub fn from(ir_op: ir::AtomicRmwOp) -> Self {
match ir_op {
ir::AtomicRmwOp::Add => AtomicRmwOp::Add,
ir::AtomicRmwOp::Sub => AtomicRmwOp::Sub,
ir::AtomicRmwOp::And => AtomicRmwOp::And,
ir::AtomicRmwOp::Or => AtomicRmwOp::Or,
ir::AtomicRmwOp::Xor => AtomicRmwOp::Xor,
ir::AtomicRmwOp::Xchg => AtomicRmwOp::Xchg,
}
}
}

View File

@@ -4,7 +4,7 @@
use crate::entity::SecondaryMap;
use crate::fx::{FxHashMap, FxHashSet};
use crate::inst_predicates::{has_side_effect_or_load, is_constant_64bit};
use crate::inst_predicates::{has_lowering_side_effect, is_constant_64bit};
use crate::ir::instructions::BranchInfo;
use crate::ir::types::I64;
use crate::ir::{
@@ -372,7 +372,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
for bb in f.layout.blocks() {
cur_color += 1;
for inst in f.layout.block_insts(bb) {
let side_effect = has_side_effect_or_load(f, inst);
let side_effect = has_lowering_side_effect(f, inst);
// Assign colors. A new color is chosen *after* any side-effecting instruction.
inst_colors[inst] = InstColor::new(cur_color);
@@ -799,15 +799,15 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
ValueDef::Result(src_inst, result_idx) => {
debug!(" -> src inst {}", src_inst);
debug!(
" -> has side effect: {}",
has_side_effect_or_load(self.f, src_inst)
" -> has lowering side effect: {}",
has_lowering_side_effect(self.f, src_inst)
);
debug!(
" -> our color is {:?}, src inst is {:?}",
self.inst_color(at_inst),
self.inst_color(src_inst)
);
if !has_side_effect_or_load(self.f, src_inst)
if !has_lowering_side_effect(self.f, src_inst)
|| self.inst_color(at_inst) == self.inst_color(src_inst)
{
Some((src_inst, result_idx))
@@ -989,8 +989,12 @@ impl<'func, I: VCodeInst> LowerCtx for Lower<'func, I> {
fn use_input_reg(&mut self, input: LowerInput) {
debug!("use_input_reg: vreg {:?} is needed", input.reg);
// We may directly return a real (machine) register when we know that register holds the
// result of an opcode (e.g. GetPinnedReg).
if input.reg.is_virtual() {
self.vreg_needed[input.reg.get_index()] = true;
}
}
fn is_reg_needed(&self, ir_inst: Inst, reg: Reg) -> bool {
self.inst_needed[ir_inst] || self.vreg_needed[reg.get_index()]

View File

@@ -133,6 +133,8 @@ pub mod adapter;
pub use adapter::*;
pub mod helpers;
pub use helpers::*;
pub mod inst_common;
pub use inst_common::*;
/// A machine instruction.
pub trait MachInst: Clone + Debug {

View File

@@ -52,11 +52,6 @@
emits native object files using the
`object <https://github.com/gimli-rs/object>`_ library.
- [cranelift-faerie](https://docs.rs/cranelift-faerie)
This crate provides a faerie-based backend for `cranelift-module`, which
emits native object files using the
`faerie <https://github.com/m4b/faerie>`_ library.
- [cranelift-simplejit](https://docs.rs/cranelift-simplejit)
This crate provides a simple JIT backend for `cranelift-module`, which
emits code and data into memory.

View File

@@ -1,21 +0,0 @@
[package]
name = "cranelift-faerie"
version = "0.66.0"
authors = ["The Cranelift Project Developers"]
description = "Emit Cranelift output to native object files with Faerie"
repository = "https://github.com/bytecodealliance/wasmtime"
documentation = "https://docs.rs/cranelift-faerie"
license = "Apache-2.0 WITH LLVM-exception"
readme = "README.md"
edition = "2018"
[dependencies]
cranelift-module = { path = "../module", version = "0.66.0" }
cranelift-codegen = { path = "../codegen", version = "0.66.0", default-features = false, features = ["std"] }
faerie = "0.15.0"
goblin = "0.1.0"
anyhow = "1.0"
target-lexicon = "0.10"
[badges]
maintenance = { status = "experimental" }

View File

@@ -1,220 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--- LLVM Exceptions to the Apache 2.0 License ----
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into an Object form of such source code, you
may redistribute such embedded portions in such Object form without complying
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
In addition, if you combine or link compiled forms of this Software with
software that is licensed under the GPLv2 ("Combined Software") and if a
court of competent jurisdiction determines that the patent provision (Section
3), the indemnity provision (Section 9) or other Section of the License
conflicts with the conditions of the GPLv2, you may retroactively and
prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined
Software.

View File

@@ -1,9 +0,0 @@
This crate contains a library that enables
[Cranelift](https://crates.io/crates/cranelift)
to emit native object (".o") files, using the
[Faerie](https://crates.io/crates/faerie) library.
DEPRECATION NOTICE: the Cranelift developer team intends to stop maintaining
the `cranelift-faerie` crate and remove it from the `wasmtime` git repository
on or after August 3, 2020. We recommend users use its successor, the
`cranelift-object` crate.

View File

@@ -1,482 +0,0 @@
//! Defines `FaerieBackend`.
use crate::container;
use anyhow::anyhow;
use cranelift_codegen::binemit::{
Addend, CodeOffset, NullStackMapSink, Reloc, RelocSink, StackMap, StackMapSink, TrapSink,
};
use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::{self, binemit, ir};
use cranelift_module::{
Backend, DataContext, DataDescription, DataId, FuncId, Init, Linkage, ModuleError,
ModuleNamespace, ModuleResult,
};
use faerie;
use std::convert::TryInto;
use std::fs::File;
use target_lexicon::Triple;
/// A builder for `FaerieBackend`.
pub struct FaerieBuilder {
isa: Box<dyn TargetIsa>,
name: String,
libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
}
impl FaerieBuilder {
/// Create a new `FaerieBuilder` using the given Cranelift target, that
/// can be passed to
/// [`Module::new`](cranelift_module::Module::new)
///
/// Faerie output requires that TargetIsa have PIC (Position Independent Code) enabled.
///
/// The `libcall_names` function provides a way to translate `cranelift_codegen`'s `ir::LibCall`
/// enum to symbols. LibCalls are inserted in the IR as part of the legalization for certain
/// floating point instructions, and for stack probes. If you don't know what to use for this
/// argument, use `cranelift_module::default_libcall_names()`.
#[deprecated(
since = "0.65.0",
note = "the Cranelift developer team intends to stop maintaining the `cranelift-faerie`
crate and remove it from the `wasmtime` git repository on or after August 3, 2020. We
recommend users use its successor, the `cranelift-object` crate."
)]
pub fn new(
isa: Box<dyn TargetIsa>,
name: String,
libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
) -> ModuleResult<Self> {
if !isa.flags().is_pic() {
return Err(ModuleError::Backend(anyhow!(
"faerie requires TargetIsa be PIC"
)));
}
Ok(Self {
isa,
name,
libcall_names,
})
}
}
/// A `FaerieBackend` implements `Backend` and emits ".o" files using the `faerie` library.
///
/// See the `FaerieBuilder` for a convenient way to construct `FaerieBackend` instances.
pub struct FaerieBackend {
isa: Box<dyn TargetIsa>,
artifact: faerie::Artifact,
libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
}
pub struct FaerieCompiledFunction {
code_length: u32,
}
impl FaerieCompiledFunction {
pub fn code_length(&self) -> u32 {
self.code_length
}
}
pub struct FaerieCompiledData {}
impl Backend for FaerieBackend {
type Builder = FaerieBuilder;
type CompiledFunction = FaerieCompiledFunction;
type CompiledData = FaerieCompiledData;
// There's no need to return individual artifacts; we're writing them into
// the output file instead.
type FinalizedFunction = ();
type FinalizedData = ();
/// The returned value here provides functions for emitting object files
/// to memory and files.
type Product = FaerieProduct;
/// Create a new `FaerieBackend` using the given Cranelift target.
fn new(builder: FaerieBuilder) -> Self {
Self {
artifact: faerie::Artifact::new(builder.isa.triple().clone(), builder.name),
isa: builder.isa,
libcall_names: builder.libcall_names,
}
}
fn isa(&self) -> &dyn TargetIsa {
&*self.isa
}
fn declare_function(&mut self, _id: FuncId, name: &str, linkage: Linkage) {
self.artifact
.declare(name, translate_function_linkage(linkage))
.expect("inconsistent declarations");
}
fn declare_data(
&mut self,
_id: DataId,
name: &str,
linkage: Linkage,
writable: bool,
tls: bool,
align: Option<u8>,
) {
assert!(!tls, "Faerie doesn't yet support TLS");
self.artifact
.declare(name, translate_data_linkage(linkage, writable, align))
.expect("inconsistent declarations");
}
fn define_function<TS>(
&mut self,
_id: FuncId,
name: &str,
ctx: &cranelift_codegen::Context,
namespace: &ModuleNamespace<Self>,
total_size: u32,
trap_sink: &mut TS,
) -> ModuleResult<FaerieCompiledFunction>
where
TS: TrapSink,
{
let mut code: Vec<u8> = vec![0; total_size as usize];
// TODO: Replace this with FaerieStackMapSink once it is implemented.
let mut stack_map_sink = NullStackMapSink {};
// Non-lexical lifetimes would obviate the braces here.
{
let mut reloc_sink = FaerieRelocSink {
triple: self.isa.triple().clone(),
artifact: &mut self.artifact,
name,
namespace,
libcall_names: &*self.libcall_names,
};
unsafe {
ctx.emit_to_memory(
&*self.isa,
code.as_mut_ptr(),
&mut reloc_sink,
trap_sink,
&mut stack_map_sink,
)
};
}
// because `define` will take ownership of code, this is our last chance
let code_length = code.len() as u32;
self.artifact
.define(name, code)
.expect("inconsistent declaration");
Ok(FaerieCompiledFunction { code_length })
}
fn define_function_bytes(
&mut self,
_id: FuncId,
name: &str,
bytes: &[u8],
_namespace: &ModuleNamespace<Self>,
) -> ModuleResult<FaerieCompiledFunction> {
let code_length: u32 = match bytes.len().try_into() {
Ok(code_length) => code_length,
_ => Err(ModuleError::FunctionTooLarge(name.to_string()))?,
};
self.artifact
.define(name, bytes.to_vec())
.expect("inconsistent declaration");
Ok(FaerieCompiledFunction { code_length })
}
fn define_data(
&mut self,
_id: DataId,
name: &str,
_writable: bool,
tls: bool,
_align: Option<u8>,
data_ctx: &DataContext,
namespace: &ModuleNamespace<Self>,
) -> ModuleResult<FaerieCompiledData> {
assert!(!tls, "Faerie doesn't yet support TLS");
let &DataDescription {
ref init,
ref function_decls,
ref data_decls,
ref function_relocs,
ref data_relocs,
ref custom_segment_section,
} = data_ctx.description();
if let Some((segment, section)) = custom_segment_section {
return Err(cranelift_module::ModuleError::Backend(anyhow::anyhow!(
"Custom section not supported by cranelift-faerie: `{}:{}`",
segment,
section
)));
}
for &(offset, id) in function_relocs {
let to = &namespace.get_function_decl(&function_decls[id]).name;
self.artifact
.link(faerie::Link {
from: name,
to,
at: u64::from(offset),
})
.map_err(|e| ModuleError::Backend(e.into()))?;
}
for &(offset, id, addend) in data_relocs {
debug_assert_eq!(
addend, 0,
"faerie doesn't support addends in data section relocations yet"
);
let to = &namespace.get_data_decl(&data_decls[id]).name;
self.artifact
.link(faerie::Link {
from: name,
to,
at: u64::from(offset),
})
.map_err(|e| ModuleError::Backend(e.into()))?;
}
match *init {
Init::Uninitialized => {
panic!("data is not initialized yet");
}
Init::Zeros { size } => {
self.artifact
.define_zero_init(name, size)
.expect("inconsistent declaration");
}
Init::Bytes { ref contents } => {
self.artifact
.define(name, contents.to_vec())
.expect("inconsistent declaration");
}
}
Ok(FaerieCompiledData {})
}
fn write_data_funcaddr(
&mut self,
_data: &mut FaerieCompiledData,
_offset: usize,
_what: ir::FuncRef,
) {
unimplemented!()
}
fn write_data_dataaddr(
&mut self,
_data: &mut FaerieCompiledData,
_offset: usize,
_what: ir::GlobalValue,
_usize: binemit::Addend,
) {
unimplemented!()
}
fn finalize_function(
&mut self,
_id: FuncId,
_func: &FaerieCompiledFunction,
_namespace: &ModuleNamespace<Self>,
) {
// Nothing to do.
}
fn get_finalized_function(&self, _func: &FaerieCompiledFunction) {
// Nothing to do.
}
fn finalize_data(
&mut self,
_id: DataId,
_data: &FaerieCompiledData,
_namespace: &ModuleNamespace<Self>,
) {
// Nothing to do.
}
fn get_finalized_data(&self, _data: &FaerieCompiledData) {
// Nothing to do.
}
fn publish(&mut self) {
// Nothing to do.
}
fn finish(self, _namespace: &ModuleNamespace<Self>) -> FaerieProduct {
FaerieProduct {
artifact: self.artifact,
}
}
}
/// This is the output of `Module`'s
/// [`finish`](../cranelift_module/struct.Module.html#method.finish) function.
/// It provides functions for writing out the object file to memory or a file.
#[derive(Debug)]
pub struct FaerieProduct {
/// Faerie artifact with all functions, data, and links from the module defined
pub artifact: faerie::Artifact,
}
impl FaerieProduct {
/// Return the name of the output file. This is the name passed into `new`.
pub fn name(&self) -> &str {
&self.artifact.name
}
/// Call `emit` on the faerie `Artifact`, producing bytes in memory.
pub fn emit(&self) -> Result<Vec<u8>, faerie::ArtifactError> {
self.artifact.emit()
}
/// Call `write` on the faerie `Artifact`, writing to a file.
pub fn write(&self, sink: File) -> Result<(), faerie::ArtifactError> {
self.artifact.write(sink)
}
}
fn translate_function_linkage(linkage: Linkage) -> faerie::Decl {
match linkage {
Linkage::Import => faerie::Decl::function_import().into(),
Linkage::Local => faerie::Decl::function().into(),
Linkage::Preemptible => faerie::Decl::function().weak().into(),
Linkage::Hidden => faerie::Decl::function().global().hidden().into(),
Linkage::Export => faerie::Decl::function().global().into(),
}
}
fn translate_data_linkage(linkage: Linkage, writable: bool, align: Option<u8>) -> faerie::Decl {
let align = align.map(u64::from);
match linkage {
Linkage::Import => faerie::Decl::data_import().into(),
Linkage::Local => faerie::Decl::data()
.with_writable(writable)
.with_align(align)
.into(),
Linkage::Preemptible => faerie::Decl::data()
.weak()
.with_writable(writable)
.with_align(align)
.into(),
Linkage::Hidden => faerie::Decl::data()
.global()
.hidden()
.with_writable(writable)
.with_align(align)
.into(),
Linkage::Export => faerie::Decl::data()
.global()
.with_writable(writable)
.with_align(align)
.into(),
}
}
struct FaerieRelocSink<'a> {
triple: Triple,
artifact: &'a mut faerie::Artifact,
name: &'a str,
namespace: &'a ModuleNamespace<'a, FaerieBackend>,
libcall_names: &'a dyn Fn(ir::LibCall) -> String,
}
impl<'a> RelocSink for FaerieRelocSink<'a> {
fn reloc_block(&mut self, _offset: CodeOffset, _reloc: Reloc, _block_offset: CodeOffset) {
unimplemented!();
}
fn reloc_external(
&mut self,
offset: CodeOffset,
_srcloc: ir::SourceLoc,
reloc: Reloc,
name: &ir::ExternalName,
addend: Addend,
) {
let ref_name: String = match *name {
ir::ExternalName::User { .. } => {
if self.namespace.is_function(name) {
self.namespace.get_function_decl(name).name.clone()
} else {
self.namespace.get_data_decl(name).name.clone()
}
}
ir::ExternalName::LibCall(ref libcall) => {
let sym = (self.libcall_names)(*libcall);
self.artifact
.declare(sym.clone(), faerie::Decl::function_import())
.expect("faerie declaration of libcall");
sym
}
_ => panic!("invalid ExternalName {}", name),
};
let (raw_reloc, raw_addend) = container::raw_relocation(reloc, &self.triple);
// TODO: Handle overflow.
let final_addend = addend + raw_addend;
let addend_i32 = final_addend as i32;
debug_assert!(i64::from(addend_i32) == final_addend);
self.artifact
.link_with(
faerie::Link {
from: self.name,
to: &ref_name,
at: u64::from(offset),
},
faerie::Reloc::Raw {
reloc: raw_reloc,
addend: addend_i32,
},
)
.expect("faerie relocation error");
}
fn reloc_jt(&mut self, _offset: CodeOffset, reloc: Reloc, _jt: ir::JumpTable) {
match reloc {
Reloc::X86PCRelRodata4 => {
// Not necessary to record this unless we are going to split apart code and its
// jumptbl/rodata.
}
_ => {
panic!("Unhandled reloc");
}
}
}
fn reloc_constant(&mut self, _offset: CodeOffset, reloc: Reloc, _jt: ir::ConstantOffset) {
match reloc {
Reloc::X86PCRelRodata4 => {
// Not necessary to record this unless we are going to split apart code and its
// jumptbl/rodata.
}
_ => {
panic!("Unhandled reloc");
}
}
}
}
#[allow(dead_code)]
struct FaerieStackMapSink<'a> {
artifact: &'a mut faerie::Artifact,
namespace: &'a ModuleNamespace<'a, FaerieBackend>,
}
/// Faerie is currently not used in SpiderMonkey. Methods are unimplemented.
impl<'a> StackMapSink for FaerieStackMapSink<'a> {
fn add_stack_map(&mut self, _: CodeOffset, _: StackMap) {
unimplemented!("faerie support for stack maps");
}
}

View File

@@ -1,65 +0,0 @@
//! Utilities for working with Faerie container formats.
use cranelift_codegen::binemit::Reloc;
use target_lexicon::{Architecture, BinaryFormat, Triple};
/// An object file format.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Format {
/// The ELF object file format.
ELF,
/// The Mach-O object file format.
MachO,
}
/// Translate from a Cranelift `Reloc` to a raw object-file-format-specific
/// relocation code and relocation-implied addend.
pub fn raw_relocation(reloc: Reloc, triple: &Triple) -> (u32, i64) {
match triple.binary_format {
BinaryFormat::Elf => {
use goblin::elf;
(
match triple.architecture {
Architecture::X86_64 => {
match reloc {
Reloc::Abs4 => elf::reloc::R_X86_64_32,
Reloc::Abs8 => elf::reloc::R_X86_64_64,
Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => elf::reloc::R_X86_64_PC32,
// TODO: Get Cranelift to tell us when we can use
// R_X86_64_GOTPCRELX/R_X86_64_REX_GOTPCRELX.
Reloc::X86CallPLTRel4 => elf::reloc::R_X86_64_PLT32,
Reloc::X86GOTPCRel4 => elf::reloc::R_X86_64_GOTPCREL,
_ => unimplemented!(),
}
}
_ => unimplemented!("unsupported architecture: {}", triple),
},
// Most ELF relocations do not include an implicit addend.
0,
)
}
BinaryFormat::Macho => {
use goblin::mach;
match triple.architecture {
Architecture::X86_64 => {
match reloc {
Reloc::Abs8 => (u32::from(mach::relocation::R_ABS), 0),
// Mach-O doesn't need us to distinguish between PC-relative calls
// and PLT calls, but it does need us to distinguish between calls
// and non-calls. And, it includes the 4-byte addend implicitly.
Reloc::X86PCRel4 => (u32::from(mach::relocation::X86_64_RELOC_SIGNED), 4),
Reloc::X86CallPCRel4 | Reloc::X86CallPLTRel4 => {
(u32::from(mach::relocation::X86_64_RELOC_BRANCH), 4)
}
Reloc::X86GOTPCRel4 => {
(u32::from(mach::relocation::X86_64_RELOC_GOT_LOAD), 4)
}
_ => unimplemented!("unsupported mach-o reloc: {}", reloc),
}
}
_ => unimplemented!("unsupported architecture: {}", triple),
}
}
_ => unimplemented!("unsupported format"),
}
}

View File

@@ -1,35 +0,0 @@
//! Top-level lib.rs for `cranelift_faerie`.
//!
//! Users of this module should not have to depend on faerie directly.
#![deny(
missing_docs,
trivial_numeric_casts,
unused_extern_crates,
unstable_features
)]
#![warn(unused_import_braces)]
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
#![cfg_attr(
feature = "cargo-clippy",
warn(
clippy::float_arithmetic,
clippy::mut_mut,
clippy::nonminimal_bool,
clippy::option_map_unwrap_or,
clippy::option_map_unwrap_or_else,
clippy::print_stdout,
clippy::unicode_not_nfc,
clippy::use_self
)
)]
mod backend;
mod container;
pub use crate::backend::{FaerieBackend, FaerieBuilder, FaerieProduct};
pub use crate::container::Format;
/// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -0,0 +1,134 @@
test compile
target aarch64
function u0:0(i8) -> f32 {
block0(v0: i8):
; check: stp fp, lr, [sp, #-16]!
; check: mov fp, sp
v1 = fcvt_from_uint.f32 v0
; check: uxtb w0, w0
; check: ucvtf s0, w0
return v1
; check: mov sp, fp
; check: ldp fp, lr, [sp], #16
; check: ret
}
function u0:0(i8) -> f64 {
block0(v0: i8):
; check: stp fp, lr, [sp, #-16]!
; check: mov fp, sp
v1 = fcvt_from_uint.f64 v0
; check: uxtb w0, w0
; check: ucvtf d0, w0
return v1
; check: mov sp, fp
; check: ldp fp, lr, [sp], #16
; check: ret
}
function u0:0(i16) -> f32 {
block0(v0: i16):
; check: stp fp, lr, [sp, #-16]!
; check: mov fp, sp
v1 = fcvt_from_uint.f32 v0
; check: uxth w0, w0
; check: ucvtf s0, w0
return v1
; check: mov sp, fp
; check: ldp fp, lr, [sp], #16
; check: ret
}
function u0:0(i16) -> f64 {
block0(v0: i16):
; check: stp fp, lr, [sp, #-16]!
; check: mov fp, sp
v1 = fcvt_from_uint.f64 v0
; check: uxth w0, w0
; check: ucvtf d0, w0
return v1
; check: mov sp, fp
; check: ldp fp, lr, [sp], #16
; check: ret
}
function u0:0(f32) -> i8 {
block0(v0: f32):
; check: stp fp, lr, [sp, #-16]!
; check: mov fp, sp
v1 = fcvt_to_uint.i8 v0
; check: fcmp s0, s0
; check: b.vc 8 ; udf
; check: ldr s1, pc+8 ; b 8 ; data.f32 -1
; check: fcmp s0, s1
; check: b.gt 8 ; udf
; check: ldr s1, pc+8 ; b 8 ; data.f32 256
; check: fcmp s0, s1
; check: b.mi 8 ; udf
; check: fcvtzu w0, s0
return v1
; check: mov sp, fp
; check: ldp fp, lr, [sp], #16
; check: ret
}
function u0:0(f64) -> i8 {
block0(v0: f64):
; check: stp fp, lr, [sp, #-16]!
; check: mov fp, sp
v1 = fcvt_to_uint.i8 v0
; check: fcmp d0, d0
; check: b.vc 8 ; udf
; check: ldr d1, pc+8 ; b 12 ; data.f64 -1
; check: fcmp d0, d1
; check: b.gt 8 ; udf
; check: ldr d1, pc+8 ; b 12 ; data.f64 256
; check: fcmp d0, d1
; check: b.mi 8 ; udf
; check: fcvtzu w0, d0
return v1
; check: mov sp, fp
; check: ldp fp, lr, [sp], #16
; check: ret
}
function u0:0(f32) -> i16 {
block0(v0: f32):
; check: stp fp, lr, [sp, #-16]!
; check: mov fp, sp
v1 = fcvt_to_uint.i16 v0
; check: fcmp s0, s0
; check: b.vc 8 ; udf
; check: ldr s1, pc+8 ; b 8 ; data.f32 -1
; check: fcmp s0, s1
; check: b.gt 8 ; udf
; check: ldr s1, pc+8 ; b 8 ; data.f32 65536
; check: fcmp s0, s1
; check: b.mi 8 ; udf
; check: fcvtzu w0, s0
return v1
; check: mov sp, fp
; check: ldp fp, lr, [sp], #16
; check: ret
}
function u0:0(f64) -> i16 {
block0(v0: f64):
; check: stp fp, lr, [sp, #-16]!
; check: mov fp, sp
v1 = fcvt_to_uint.i16 v0
; check: fcmp d0, d0
; check: b.vc 8 ; udf
; check: ldr d1, pc+8 ; b 12 ; data.f64 -1
; check: fcmp d0, d1
; check: b.gt 8 ; udf
; check: ldr d1, pc+8 ; b 12 ; data.f64 65536
; check: fcmp d0, d1
; check: b.mi 8 ; udf
; check: fcvtzu w0, d0
return v1
; check: mov sp, fp
; check: ldp fp, lr, [sp], #16
; check: ret
}

View File

@@ -0,0 +1,32 @@
; Test that `put_input_in_rse` doesn't try to put the input of the `iconst` into a register, which
; would result in an out-of-bounds panic. (#2147)
test compile
target aarch64
function u0:0() -> i8 system_v {
block0:
v0 = iconst.i16 0xddcc
v1 = icmp.i16 ne v0, v0
v2 = bint.i8 v1
return v2
}
; check: VCode_ShowWithRRU {{
; nextln: Entry block: 0
; nextln: Block 0:
; nextln: (original IR block: block0)
; nextln: (instruction range: 0 .. 11)
; nextln: Inst 0: stp fp, lr, [sp, #-16]!
; nextln: Inst 1: mov fp, sp
; nextln: Inst 2: movz x0, #56780
; nextln: Inst 3: uxth w0, w0
; nextln: Inst 4: movz x1, #56780
; nextln: Inst 5: subs wzr, w0, w1, UXTH
; nextln: Inst 6: cset x0, ne
; nextln: Inst 7: and w0, w0, #1
; nextln: Inst 8: mov sp, fp
; nextln: Inst 9: ldp fp, lr, [sp], #16
; nextln: Inst 10: ret
; nextln: }}

View File

@@ -15,9 +15,6 @@ following `Backend` implementations:
code to memory for direct execution.
- `ObjectBackend`, provided by [cranelift-object], which emits native
object files.
- `FaerieBackend`, provided by [cranelift-faerie], which emits native
object files.
[cranelift-simplejit]: https://crates.io/crates/cranelift-simplejit
[cranelift-object]: https://crates.io/crates/cranelift-object
[cranelift-faerie]: https://crates.io/crates/cranelift-faerie

View File

@@ -22,12 +22,9 @@ use std::string::String;
/// the contents of a `Module` to memory which can be directly executed.
/// - `ObjectBackend`, defined in [cranelift-object], which writes the
/// contents of a `Module` out as a native object file.
/// - `FaerieBackend`, defined in [cranelift-faerie], which writes the
/// contents of a `Module` out as a native object file.
///
/// [cranelift-simplejit]: https://docs.rs/cranelift-simplejit/
/// [cranelift-object]: https://docs.rs/cranelift-object/
/// [cranelift-faerie]: https://docs.rs/cranelift-faerie/
pub trait Backend
where
Self: marker::Sized,

View File

@@ -12,7 +12,7 @@ edition = "2018"
[dependencies]
cranelift-module = { path = "../module", version = "0.66.0" }
cranelift-codegen = { path = "../codegen", version = "0.66.0", default-features = false, features = ["std"] }
object = { version = "0.20", default-features = false, features = ["write"] }
object = { version = "0.21.1", default-features = false, features = ["write"] }
target-lexicon = "0.10"
anyhow = "1.0"

View File

@@ -0,0 +1,19 @@
[package]
name = "wasmtime-cranelift"
version = "0.19.0"
authors = ["The Wasmtime Project Developers"]
description = "Integration between Cranelift and Wasmtime"
license = "Apache-2.0 WITH LLVM-exception"
repository = "https://github.com/bytecodealliance/wasmtime"
documentation = "https://docs.rs/wasmtime-cranelift/"
categories = ["wasm"]
keywords = ["webassembly", "wasm"]
readme = "README.md"
edition = "2018"
[dependencies]
wasmtime-environ = { path = "../environ", version = "0.19.0" }
cranelift-wasm = { path = "../../cranelift/wasm", version = "0.66.0" }
cranelift-codegen = { path = "../../cranelift/codegen", version = "0.66.0" }
cranelift-frontend = { path = "../../cranelift/frontend", version = "0.66.0" }
cranelift-entity = { path = "../../cranelift/entity", version = "0.66.0" }

View File

@@ -0,0 +1,4 @@
# `wasmtime-cranelfit`
This crate provides an implementation of the `Compiler` trait which is
connected to Cranelift.

View File

@@ -1,6 +1,3 @@
use crate::module::{MemoryPlan, MemoryStyle, TableStyle};
use crate::vmoffsets::VMOffsets;
use crate::{Module, Tunables, INTERRUPTED, WASM_PAGE_SIZE};
use cranelift_codegen::cursor::FuncCursor;
use cranelift_codegen::ir;
use cranelift_codegen::ir::condcodes::*;
@@ -14,20 +11,18 @@ use cranelift_wasm::{
self, FuncIndex, GlobalIndex, GlobalVariable, MemoryIndex, SignatureIndex, TableIndex,
TargetEnvironment, WasmError, WasmResult, WasmType,
};
#[cfg(feature = "lightbeam")]
use cranelift_wasm::{DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex};
use std::convert::TryFrom;
use wasmtime_environ::{
BuiltinFunctionIndex, MemoryPlan, MemoryStyle, Module, TableStyle, Tunables, VMOffsets,
INTERRUPTED, WASM_PAGE_SIZE,
};
/// Compute an `ir::ExternalName` for a given wasm function index.
pub fn get_func_name(func_index: FuncIndex) -> ir::ExternalName {
ir::ExternalName::user(0, func_index.as_u32())
}
/// An index type for builtin functions.
#[derive(Copy, Clone, Debug)]
pub struct BuiltinFunctionIndex(u32);
macro_rules! declare_builtin_functions {
macro_rules! declare_function_signatures {
(
$(
$( #[$attr:meta] )*
@@ -91,111 +86,10 @@ macro_rules! declare_builtin_functions {
}
)*
}
impl BuiltinFunctionIndex {
declare_builtin_functions!(
@indices;
0;
$( $( #[$attr] )* $name; )*
);
}
};
// Base case: no more indices to declare, so define the total number of
// function indices.
(
@indices;
$len:expr;
) => {
/// Returns the total number of builtin functions.
pub const fn builtin_functions_total_number() -> u32 {
$len
}
};
// Recursive case: declare the next index, and then keep declaring the rest of
// the indices.
(
@indices;
$index:expr;
$( #[$this_attr:meta] )*
$this_name:ident;
$(
$( #[$rest_attr:meta] )*
$rest_name:ident;
)*
) => {
$( #[$this_attr] )*
pub const fn $this_name() -> Self {
Self($index)
}
declare_builtin_functions!(
@indices;
($index + 1);
$( $( #[$rest_attr] )* $rest_name; )*
);
}
}
declare_builtin_functions! {
/// Returns an index for wasm's `memory.grow` builtin function.
memory32_grow(vmctx, i32, i32) -> (i32);
/// Returns an index for wasm's imported `memory.grow` builtin function.
imported_memory32_grow(vmctx, i32, i32) -> (i32);
/// Returns an index for wasm's `memory.size` builtin function.
memory32_size(vmctx, i32) -> (i32);
/// Returns an index for wasm's imported `memory.size` builtin function.
imported_memory32_size(vmctx, i32) -> (i32);
/// Returns an index for wasm's `table.copy` when both tables are locally
/// defined.
table_copy(vmctx, i32, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `table.init`.
table_init(vmctx, i32, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `elem.drop`.
elem_drop(vmctx, i32) -> ();
/// Returns an index for wasm's `memory.copy` for locally defined memories.
defined_memory_copy(vmctx, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `memory.copy` for imported memories.
imported_memory_copy(vmctx, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `memory.fill` for locally defined memories.
memory_fill(vmctx, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `memory.fill` for imported memories.
imported_memory_fill(vmctx, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `memory.init` instruction.
memory_init(vmctx, i32, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `data.drop` instruction.
data_drop(vmctx, i32) -> ();
/// Returns an index for Wasm's `table.grow` instruction for `funcref`s.
table_grow_funcref(vmctx, i32, i32, pointer) -> (i32);
/// Returns an index for Wasm's `table.grow` instruction for `externref`s.
table_grow_externref(vmctx, i32, i32, reference) -> (i32);
/// Returns an index for Wasm's `table.fill` instruction for `externref`s.
table_fill_externref(vmctx, i32, i32, reference, i32) -> ();
/// Returns an index for Wasm's `table.fill` instruction for `funcref`s.
table_fill_funcref(vmctx, i32, i32, pointer, i32) -> ();
/// Returns an index to drop a `VMExternRef`.
drop_externref(pointer) -> ();
/// Returns an index to do a GC and then insert a `VMExternRef` into the
/// `VMExternRefActivationsTable`.
activations_table_insert_with_gc(vmctx, reference) -> ();
/// Returns an index for Wasm's `global.get` instruction for `externref`s.
externref_global_get(vmctx, i32) -> (reference);
/// Returns an index for Wasm's `global.get` instruction for `externref`s.
externref_global_set(vmctx, i32, reference) -> ();
}
impl BuiltinFunctionIndex {
/// Create a new `BuiltinFunctionIndex` from its index
pub const fn from_u32(i: u32) -> Self {
Self(i)
}
/// Return the index as an u32 number.
pub const fn index(&self) -> u32 {
self.0
}
}
wasmtime_environ::foreach_builtin_function!(declare_function_signatures);
/// The `FuncEnvironment` implementation for use by the `ModuleEnvironment`.
pub struct FuncEnvironment<'module_environment> {
@@ -464,153 +358,13 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
}
}
// TODO: This is necessary as if Lightbeam used `FuncEnvironment` directly it would cause
// a circular dependency graph. We should extract common types out into a separate
// crate that Lightbeam can use but until then we need this trait.
#[cfg(feature = "lightbeam")]
impl lightbeam::ModuleContext for FuncEnvironment<'_> {
type Signature = ir::Signature;
type GlobalType = ir::Type;
fn func_index(&self, defined_func_index: u32) -> u32 {
self.module
.func_index(DefinedFuncIndex::from_u32(defined_func_index))
.as_u32()
}
fn defined_func_index(&self, func_index: u32) -> Option<u32> {
self.module
.defined_func_index(FuncIndex::from_u32(func_index))
.map(DefinedFuncIndex::as_u32)
}
fn defined_global_index(&self, global_index: u32) -> Option<u32> {
self.module
.defined_global_index(GlobalIndex::from_u32(global_index))
.map(DefinedGlobalIndex::as_u32)
}
fn global_type(&self, global_index: u32) -> &Self::GlobalType {
&self.module.globals[GlobalIndex::from_u32(global_index)].ty
}
fn func_type_index(&self, func_idx: u32) -> u32 {
self.module.functions[FuncIndex::from_u32(func_idx)].as_u32()
}
fn signature(&self, index: u32) -> &Self::Signature {
&self.module.signatures[SignatureIndex::from_u32(index)].1
}
fn defined_table_index(&self, table_index: u32) -> Option<u32> {
self.module
.defined_table_index(TableIndex::from_u32(table_index))
.map(DefinedTableIndex::as_u32)
}
fn defined_memory_index(&self, memory_index: u32) -> Option<u32> {
self.module
.defined_memory_index(MemoryIndex::from_u32(memory_index))
.map(DefinedMemoryIndex::as_u32)
}
fn vmctx_builtin_function(&self, func_index: u32) -> u32 {
self.offsets
.vmctx_builtin_function(BuiltinFunctionIndex::from_u32(func_index))
}
fn vmctx_vmfunction_import_body(&self, func_index: u32) -> u32 {
self.offsets
.vmctx_vmfunction_import_body(FuncIndex::from_u32(func_index))
}
fn vmctx_vmfunction_import_vmctx(&self, func_index: u32) -> u32 {
self.offsets
.vmctx_vmfunction_import_vmctx(FuncIndex::from_u32(func_index))
}
fn vmctx_vmglobal_import_from(&self, global_index: u32) -> u32 {
self.offsets
.vmctx_vmglobal_import_from(GlobalIndex::from_u32(global_index))
}
fn vmctx_vmglobal_definition(&self, defined_global_index: u32) -> u32 {
self.offsets
.vmctx_vmglobal_definition(DefinedGlobalIndex::from_u32(defined_global_index))
}
fn vmctx_vmmemory_import_from(&self, memory_index: u32) -> u32 {
self.offsets
.vmctx_vmmemory_import_from(MemoryIndex::from_u32(memory_index))
}
fn vmctx_vmmemory_definition(&self, defined_memory_index: u32) -> u32 {
self.offsets
.vmctx_vmmemory_definition(DefinedMemoryIndex::from_u32(defined_memory_index))
}
fn vmctx_vmmemory_definition_base(&self, defined_memory_index: u32) -> u32 {
self.offsets
.vmctx_vmmemory_definition_base(DefinedMemoryIndex::from_u32(defined_memory_index))
}
fn vmctx_vmmemory_definition_current_length(&self, defined_memory_index: u32) -> u32 {
self.offsets
.vmctx_vmmemory_definition_current_length(DefinedMemoryIndex::from_u32(
defined_memory_index,
))
}
fn vmmemory_definition_base(&self) -> u8 {
self.offsets.vmmemory_definition_base()
}
fn vmmemory_definition_current_length(&self) -> u8 {
self.offsets.vmmemory_definition_current_length()
}
fn vmctx_vmtable_import_from(&self, table_index: u32) -> u32 {
self.offsets
.vmctx_vmtable_import_from(TableIndex::from_u32(table_index))
}
fn vmctx_vmtable_definition(&self, defined_table_index: u32) -> u32 {
self.offsets
.vmctx_vmtable_definition(DefinedTableIndex::from_u32(defined_table_index))
}
fn vmctx_vmtable_definition_base(&self, defined_table_index: u32) -> u32 {
self.offsets
.vmctx_vmtable_definition_base(DefinedTableIndex::from_u32(defined_table_index))
}
fn vmctx_vmtable_definition_current_elements(&self, defined_table_index: u32) -> u32 {
self.offsets
.vmctx_vmtable_definition_current_elements(DefinedTableIndex::from_u32(
defined_table_index,
))
}
fn vmtable_definition_base(&self) -> u8 {
self.offsets.vmtable_definition_base()
}
fn vmtable_definition_current_elements(&self) -> u8 {
self.offsets.vmtable_definition_current_elements()
}
fn vmcaller_checked_anyfunc_type_index(&self) -> u8 {
self.offsets.vmcaller_checked_anyfunc_type_index()
}
fn vmcaller_checked_anyfunc_func_ptr(&self) -> u8 {
self.offsets.vmcaller_checked_anyfunc_func_ptr()
}
fn vmcaller_checked_anyfunc_vmctx(&self) -> u8 {
self.offsets.vmcaller_checked_anyfunc_vmctx()
}
fn size_of_vmcaller_checked_anyfunc(&self) -> u8 {
self.offsets.size_of_vmcaller_checked_anyfunc()
}
fn vmctx_vmshared_signature_id(&self, signature_idx: u32) -> u32 {
self.offsets
.vmctx_vmshared_signature_id(SignatureIndex::from_u32(signature_idx))
}
// TODO: type of a global
}
impl<'module_environment> TargetEnvironment for FuncEnvironment<'module_environment> {
fn target_config(&self) -> TargetFrontendConfig {
self.target_config
}
fn reference_type(&self, ty: WasmType) -> ir::Type {
crate::reference_type(ty, self.pointer_type())
wasmtime_environ::reference_type(ty, self.pointer_type())
}
}

View File

@@ -1,4 +1,7 @@
//! Support for compiling with Cranelift.
//!
//! This crate provides an implementation of [`Compiler`] in the form of
//! [`Cranelift`].
// # How does Wasmtime prevent stack overflow?
//
@@ -86,13 +89,6 @@
// assume no valid stack pointer will ever be `usize::max_value() - 32k`.
use crate::func_environ::{get_func_name, FuncEnvironment};
use crate::Compiler;
use crate::{
CompileError, CompiledFunction, Relocation, RelocationTarget, StackMapInformation,
TrapInformation,
};
use crate::{FunctionAddressMap, InstructionAddressMap};
use crate::{FunctionBodyData, ModuleTranslation};
use cranelift_codegen::ir::{self, ExternalName};
use cranelift_codegen::machinst::buffer::MachSrcLoc;
use cranelift_codegen::print_errors::pretty_error;
@@ -100,14 +96,21 @@ use cranelift_codegen::{binemit, isa, Context};
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator};
use std::convert::TryFrom;
use std::sync::Mutex;
use wasmtime_environ::{
CompileError, CompiledFunction, Compiler, FunctionAddressMap, FunctionBodyData,
InstructionAddressMap, ModuleTranslation, Relocation, RelocationTarget, StackMapInformation,
TrapInformation,
};
mod func_environ;
/// Implementation of a relocation sink that just saves all the information for later
pub struct RelocSink {
struct RelocSink {
/// Current function index.
func_index: FuncIndex,
/// Relocations recorded for the function.
pub func_relocs: Vec<Relocation>,
func_relocs: Vec<Relocation>,
}
impl binemit::RelocSink for RelocSink {
@@ -166,7 +169,7 @@ impl binemit::RelocSink for RelocSink {
impl RelocSink {
/// Return a new `RelocSink` instance.
pub fn new(func_index: FuncIndex) -> Self {
fn new(func_index: FuncIndex) -> Self {
Self {
func_index,
func_relocs: Vec::new(),
@@ -176,14 +179,14 @@ impl RelocSink {
/// Implementation of a trap sink that simply stores all trap info in-memory
#[derive(Default)]
pub struct TrapSink {
struct TrapSink {
/// The in-memory vector of trap info
pub traps: Vec<TrapInformation>,
traps: Vec<TrapInformation>,
}
impl TrapSink {
/// Create a new `TrapSink`
pub fn new() -> Self {
fn new() -> Self {
Self::default()
}
}

View File

@@ -14,7 +14,7 @@ edition = "2018"
[dependencies]
gimli = "0.21.0"
wasmparser = "0.59.0"
object = { version = "0.20", default-features = false, features = ["read", "write"] }
object = { version = "0.21.1", default-features = false, features = ["read", "write"] }
wasmtime-environ = { path = "../environ", version = "0.19.0" }
target-lexicon = { version = "0.10.0", default-features = false }
anyhow = "1.0"

View File

@@ -64,6 +64,10 @@ impl ExpressionWriter {
write::Writer::write_u8(&mut self.0, b)
}
pub fn write_u32(&mut self, b: u32) -> write::Result<()> {
write::Writer::write_u32(&mut self.0, b)
}
pub fn write_uleb128(&mut self, i: u64) -> write::Result<()> {
write::Writer::write_uleb128(&mut self.0, i)
}
@@ -196,8 +200,8 @@ fn append_memory_deref(
}
writer.write_op(gimli::constants::DW_OP_deref)?;
writer.write_op(gimli::constants::DW_OP_swap)?;
writer.write_op(gimli::constants::DW_OP_constu)?;
writer.write_uleb128(0xffff_ffff)?;
writer.write_op(gimli::constants::DW_OP_const4u)?;
writer.write_u32(0xffff_ffff)?;
writer.write_op(gimli::constants::DW_OP_and)?;
writer.write_op(gimli::constants::DW_OP_plus)?;
buf.extend(writer.into_vec());
@@ -418,7 +422,7 @@ where
let op = Operation::parse(&mut pc, encoding)?;
match op {
Operation::FrameOffset { offset } => {
// Expand DW_OP_fpreg into frame location and DW_OP_plus_uconst.
// Expand DW_OP_fbreg into frame location and DW_OP_plus_uconst.
if frame_base.is_some() {
// Add frame base expressions.
flush_code_chunk!();
@@ -454,7 +458,8 @@ where
Operation::Deref { .. } => {
flush_code_chunk!();
parts.push(CompiledExpressionPart::Deref);
continue;
// Don't re-enter the loop here (i.e. continue), because the
// DW_OP_deref still needs to be kept.
}
_ => {
return Ok(None);
@@ -680,8 +685,7 @@ mod tests {
let e = expression!(DW_OP_WASM_location, 0x0, 3, DW_OP_stack_value);
let fe = compile_expression(&e, DWARF_ENCODING, None).expect("non-error");
// DW_OP_fpreg 0x12
let e = expression!(0x91, 0x12);
let e = expression!(DW_OP_fbreg, 0x12);
let ce = compile_expression(&e, DWARF_ENCODING, fe.as_ref())
.expect("non-error")
.expect("expression");
@@ -721,7 +725,7 @@ mod tests {
},
CompiledExpressionPart::Code(vec![35, 5]),
CompiledExpressionPart::Deref,
CompiledExpressionPart::Code(vec![159])
CompiledExpressionPart::Code(vec![6, 159])
],
need_deref: false
}

View File

@@ -15,10 +15,8 @@ edition = "2018"
anyhow = "1.0"
cranelift-codegen = { path = "../../cranelift/codegen", version = "0.66.0", features = ["enable-serde"] }
cranelift-entity = { path = "../../cranelift/entity", version = "0.66.0", features = ["enable-serde"] }
cranelift-frontend = { path = "../../cranelift/frontend", version = "0.66.0" }
cranelift-wasm = { path = "../../cranelift/wasm", version = "0.66.0", features = ["enable-serde"] }
wasmparser = "0.59.0"
lightbeam = { path = "../lightbeam", optional = true, version = "0.19.0" }
indexmap = { version = "1.0.2", features = ["serde-1"] }
thiserror = "1.0.4"
serde = { version = "1.0.94", features = ["derive"] }

View File

@@ -0,0 +1,123 @@
/// Helper macro to iterate over all builtin functions and their signatures.
#[macro_export]
macro_rules! foreach_builtin_function {
($mac:ident) => {
$mac! {
/// Returns an index for wasm's `memory.grow` builtin function.
memory32_grow(vmctx, i32, i32) -> (i32);
/// Returns an index for wasm's imported `memory.grow` builtin function.
imported_memory32_grow(vmctx, i32, i32) -> (i32);
/// Returns an index for wasm's `memory.size` builtin function.
memory32_size(vmctx, i32) -> (i32);
/// Returns an index for wasm's imported `memory.size` builtin function.
imported_memory32_size(vmctx, i32) -> (i32);
/// Returns an index for wasm's `table.copy` when both tables are locally
/// defined.
table_copy(vmctx, i32, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `table.init`.
table_init(vmctx, i32, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `elem.drop`.
elem_drop(vmctx, i32) -> ();
/// Returns an index for wasm's `memory.copy` for locally defined memories.
defined_memory_copy(vmctx, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `memory.copy` for imported memories.
imported_memory_copy(vmctx, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `memory.fill` for locally defined memories.
memory_fill(vmctx, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `memory.fill` for imported memories.
imported_memory_fill(vmctx, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `memory.init` instruction.
memory_init(vmctx, i32, i32, i32, i32, i32) -> ();
/// Returns an index for wasm's `data.drop` instruction.
data_drop(vmctx, i32) -> ();
/// Returns an index for Wasm's `table.grow` instruction for `funcref`s.
table_grow_funcref(vmctx, i32, i32, pointer) -> (i32);
/// Returns an index for Wasm's `table.grow` instruction for `externref`s.
table_grow_externref(vmctx, i32, i32, reference) -> (i32);
/// Returns an index for Wasm's `table.fill` instruction for `externref`s.
table_fill_externref(vmctx, i32, i32, reference, i32) -> ();
/// Returns an index for Wasm's `table.fill` instruction for `funcref`s.
table_fill_funcref(vmctx, i32, i32, pointer, i32) -> ();
/// Returns an index to drop a `VMExternRef`.
drop_externref(pointer) -> ();
/// Returns an index to do a GC and then insert a `VMExternRef` into the
/// `VMExternRefActivationsTable`.
activations_table_insert_with_gc(vmctx, reference) -> ();
/// Returns an index for Wasm's `global.get` instruction for `externref`s.
externref_global_get(vmctx, i32) -> (reference);
/// Returns an index for Wasm's `global.get` instruction for `externref`s.
externref_global_set(vmctx, i32, reference) -> ();
}
};
}
/// An index type for builtin functions.
#[derive(Copy, Clone, Debug)]
pub struct BuiltinFunctionIndex(u32);
impl BuiltinFunctionIndex {
/// Create a new `BuiltinFunctionIndex` from its index
pub const fn from_u32(i: u32) -> Self {
Self(i)
}
/// Return the index as an u32 number.
pub const fn index(&self) -> u32 {
self.0
}
}
macro_rules! declare_indexes {
(
$(
$( #[$attr:meta] )*
$name:ident( $( $param:ident ),* ) -> ( $( $result:ident ),* );
)*
) => {
impl BuiltinFunctionIndex {
declare_indexes!(
@indices;
0;
$( $( #[$attr] )* $name; )*
);
}
};
// Base case: no more indices to declare, so define the total number of
// function indices.
(
@indices;
$len:expr;
) => {
/// Returns the total number of builtin functions.
pub const fn builtin_functions_total_number() -> u32 {
$len
}
};
// Recursive case: declare the next index, and then keep declaring the rest of
// the indices.
(
@indices;
$index:expr;
$( #[$this_attr:meta] )*
$this_name:ident;
$(
$( #[$rest_attr:meta] )*
$rest_name:ident;
)*
) => {
$( #[$this_attr] )*
pub const fn $this_name() -> Self {
Self($index)
}
declare_indexes!(
@indices;
($index + 1);
$( $( #[$rest_attr] )* $rest_name; )*
);
}
}
foreach_builtin_function!(declare_indexes);

View File

@@ -25,25 +25,19 @@
)]
mod address_map;
mod builtin;
mod compilation;
mod data_structures;
mod func_environ;
mod module;
mod module_environ;
mod tunables;
mod vmoffsets;
pub mod cranelift;
#[cfg(feature = "lightbeam")]
pub mod lightbeam;
pub use crate::address_map::*;
pub use crate::builtin::*;
pub use crate::compilation::*;
pub use crate::cranelift::Cranelift;
pub use crate::data_structures::*;
pub use crate::func_environ::BuiltinFunctionIndex;
#[cfg(feature = "lightbeam")]
pub use crate::lightbeam::Lightbeam;
// pub use crate::func_environ::BuiltinFunctionIndex;
pub use crate::module::{
EntityIndex, MemoryPlan, MemoryStyle, Module, TableElements, TablePlan, TableStyle,
};
@@ -60,10 +54,8 @@ pub const WASM_MAX_PAGES: u32 = 0x10000;
/// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub(crate) fn reference_type(
wasm_ty: cranelift_wasm::WasmType,
pointer_type: ir::Type,
) -> ir::Type {
/// Returns the reference type to use for the provided wasm type.
pub fn reference_type(wasm_ty: cranelift_wasm::WasmType, pointer_type: ir::Type) -> ir::Type {
match wasm_ty {
cranelift_wasm::WasmType::FuncRef => pointer_type,
cranelift_wasm::WasmType::ExternRef => match pointer_type {

View File

@@ -1,71 +0,0 @@
//! Support for compiling with Lightbeam.
use crate::compilation::{CompileError, CompiledFunction, Compiler};
use crate::cranelift::{RelocSink, TrapSink};
use crate::func_environ::FuncEnvironment;
use crate::{FunctionBodyData, ModuleTranslation};
use cranelift_codegen::isa;
use cranelift_wasm::DefinedFuncIndex;
use lightbeam::{CodeGenSession, NullOffsetSink, Sinks};
/// A compiler that compiles a WebAssembly module with Lightbeam, directly translating the Wasm file.
pub struct Lightbeam;
impl Compiler for Lightbeam {
fn compile_function(
&self,
translation: &ModuleTranslation,
i: DefinedFuncIndex,
function_body: &FunctionBodyData<'_>,
isa: &dyn isa::TargetIsa,
) -> Result<CompiledFunction, CompileError> {
if translation.tunables.debug_info {
return Err(CompileError::DebugInfoNotSupported);
}
let func_index = translation.module.func_index(i);
let env = FuncEnvironment::new(
isa.frontend_config(),
&translation.module,
&translation.tunables,
);
let mut codegen_session: CodeGenSession<_> = CodeGenSession::new(
translation.function_body_inputs.len() as u32,
&env,
lightbeam::microwasm::I32,
);
let mut reloc_sink = RelocSink::new(func_index);
let mut trap_sink = TrapSink::new();
lightbeam::translate_function(
&mut codegen_session,
Sinks {
relocs: &mut reloc_sink,
traps: &mut trap_sink,
offsets: &mut NullOffsetSink,
},
i.as_u32(),
wasmparser::FunctionBody::new(0, function_body.data),
)
.map_err(|e| CompileError::Codegen(format!("Failed to translate function: {}", e)))?;
let code_section = codegen_session
.into_translated_code_section()
.map_err(|e| CompileError::Codegen(format!("Failed to generate output code: {}", e)))?;
Ok(CompiledFunction {
// TODO: try to remove copy here (?)
body: code_section.buffer().to_vec(),
traps: trap_sink.traps,
relocations: reloc_sink.func_relocs,
// not implemented for lightbeam currently
unwind_info: None,
stack_maps: Default::default(),
stack_slots: Default::default(),
value_labels_ranges: Default::default(),
address_map: Default::default(),
jt_offsets: Default::default(),
})
}
}

View File

@@ -19,6 +19,8 @@ cranelift-native = { path = "../../cranelift/native", version = "0.66.0" }
cranelift-frontend = { path = "../../cranelift/frontend", version = "0.66.0" }
wasmtime-environ = { path = "../environ", version = "0.19.0" }
wasmtime-runtime = { path = "../runtime", version = "0.19.0" }
wasmtime-cranelift = { path = "../cranelift", version = "0.19.0" }
wasmtime-lightbeam = { path = "../lightbeam/wasmtime", version = "0.19.0", optional = true }
wasmtime-debug = { path = "../debug", version = "0.19.0" }
wasmtime-profiling = { path = "../profiling", version = "0.19.0" }
wasmtime-obj = { path = "../obj", version = "0.19.0" }
@@ -32,14 +34,14 @@ anyhow = "1.0"
cfg-if = "0.1.9"
log = "0.4"
gimli = { version = "0.21.0", default-features = false, features = ["write"] }
object = { version = "0.20", default-features = false, features = ["write"] }
object = { version = "0.21.1", default-features = false, features = ["write"] }
serde = { version = "1.0.94", features = ["derive"] }
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3.8", features = ["winnt", "impl-default"] }
[features]
lightbeam = ["wasmtime-environ/lightbeam"]
lightbeam = ["wasmtime-lightbeam"]
jitdump = ["wasmtime-profiling/jitdump"]
vtune = ["wasmtime-profiling/vtune"]
parallel-compilation = ["rayon"]

View File

@@ -50,10 +50,10 @@ impl Compiler {
strategy,
compiler: match strategy {
CompilationStrategy::Auto | CompilationStrategy::Cranelift => {
Box::new(wasmtime_environ::cranelift::Cranelift::default())
Box::new(wasmtime_cranelift::Cranelift::default())
}
#[cfg(feature = "lightbeam")]
CompilationStrategy::Lightbeam => Box::new(wasmtime_environ::lightbeam::Lightbeam),
CompilationStrategy::Lightbeam => Box::new(wasmtime_lightbeam::Lightbeam),
},
tunables,
}

View File

@@ -0,0 +1,18 @@
[package]
name = "wasmtime-lightbeam"
version = "0.19.0"
authors = ["The Wasmtime Project Developers"]
description = "Integration between Lightbeam and Wasmtime"
license = "Apache-2.0 WITH LLVM-exception"
repository = "https://github.com/bytecodealliance/wasmtime"
documentation = "https://docs.rs/wasmtime-lightbeam/"
categories = ["wasm"]
keywords = ["webassembly", "wasm"]
readme = "README.md"
edition = "2018"
[dependencies]
lightbeam = { path = "..", version = "0.19.0" }
wasmparser = "0.59"
cranelift-codegen = { path = "../../../cranelift/codegen", version = "0.66.0" }
wasmtime-environ = { path = "../../environ", version = "0.19.0" }

View File

@@ -0,0 +1,4 @@
# `wasmtime-lightbeam`
This crate provides an implementation of the `Compiler` trait which is
connected to Lightbeam.

View File

@@ -0,0 +1,334 @@
//! Support for compiling with Lightbeam.
//!
//! This crates provides an implementation of [`Compiler`] in the form of
//! [`Lightbeam`].
use cranelift_codegen::binemit;
use cranelift_codegen::ir::{self, ExternalName};
use cranelift_codegen::isa;
use lightbeam::{CodeGenSession, NullOffsetSink, Sinks};
use wasmtime_environ::wasm::{
DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex,
GlobalIndex, MemoryIndex, SignatureIndex, TableIndex,
};
use wasmtime_environ::{
BuiltinFunctionIndex, CompileError, CompiledFunction, Compiler, FunctionBodyData, Module,
ModuleTranslation, Relocation, RelocationTarget, TrapInformation, VMOffsets,
};
/// A compiler that compiles a WebAssembly module with Lightbeam, directly translating the Wasm file.
pub struct Lightbeam;
impl Compiler for Lightbeam {
fn compile_function(
&self,
translation: &ModuleTranslation,
i: DefinedFuncIndex,
function_body: &FunctionBodyData<'_>,
isa: &dyn isa::TargetIsa,
) -> Result<CompiledFunction, CompileError> {
if translation.tunables.debug_info {
return Err(CompileError::DebugInfoNotSupported);
}
let func_index = translation.module.func_index(i);
let env = FuncEnvironment::new(isa.frontend_config().pointer_bytes(), &translation.module);
let mut codegen_session: CodeGenSession<_> = CodeGenSession::new(
translation.function_body_inputs.len() as u32,
&env,
lightbeam::microwasm::I32,
);
let mut reloc_sink = RelocSink::new(func_index);
let mut trap_sink = TrapSink::new();
lightbeam::translate_function(
&mut codegen_session,
Sinks {
relocs: &mut reloc_sink,
traps: &mut trap_sink,
offsets: &mut NullOffsetSink,
},
i.as_u32(),
wasmparser::FunctionBody::new(0, function_body.data),
)
.map_err(|e| CompileError::Codegen(format!("Failed to translate function: {}", e)))?;
let code_section = codegen_session
.into_translated_code_section()
.map_err(|e| CompileError::Codegen(format!("Failed to generate output code: {}", e)))?;
Ok(CompiledFunction {
// TODO: try to remove copy here (?)
body: code_section.buffer().to_vec(),
traps: trap_sink.traps,
relocations: reloc_sink.func_relocs,
// not implemented for lightbeam currently
unwind_info: None,
stack_maps: Default::default(),
stack_slots: Default::default(),
value_labels_ranges: Default::default(),
address_map: Default::default(),
jt_offsets: Default::default(),
})
}
}
/// Implementation of a relocation sink that just saves all the information for later
struct RelocSink {
/// Current function index.
func_index: FuncIndex,
/// Relocations recorded for the function.
func_relocs: Vec<Relocation>,
}
impl binemit::RelocSink for RelocSink {
fn reloc_block(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_block_offset: binemit::CodeOffset,
) {
// This should use the `offsets` field of `ir::Function`.
panic!("block headers not yet implemented");
}
fn reloc_external(
&mut self,
offset: binemit::CodeOffset,
_srcloc: ir::SourceLoc,
reloc: binemit::Reloc,
name: &ExternalName,
addend: binemit::Addend,
) {
let reloc_target = if let ExternalName::User { namespace, index } = *name {
debug_assert_eq!(namespace, 0);
RelocationTarget::UserFunc(FuncIndex::from_u32(index))
} else if let ExternalName::LibCall(libcall) = *name {
RelocationTarget::LibCall(libcall)
} else {
panic!("unrecognized external name")
};
self.func_relocs.push(Relocation {
reloc,
reloc_target,
offset,
addend,
});
}
fn reloc_constant(
&mut self,
_code_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_constant_offset: ir::ConstantOffset,
) {
// Do nothing for now: cranelift emits constant data after the function code and also emits
// function code with correct relative offsets to the constant data.
}
fn reloc_jt(&mut self, offset: binemit::CodeOffset, reloc: binemit::Reloc, jt: ir::JumpTable) {
self.func_relocs.push(Relocation {
reloc,
reloc_target: RelocationTarget::JumpTable(self.func_index, jt),
offset,
addend: 0,
});
}
}
impl RelocSink {
/// Return a new `RelocSink` instance.
fn new(func_index: FuncIndex) -> Self {
Self {
func_index,
func_relocs: Vec::new(),
}
}
}
/// Implementation of a trap sink that simply stores all trap info in-memory
#[derive(Default)]
struct TrapSink {
/// The in-memory vector of trap info
traps: Vec<TrapInformation>,
}
impl TrapSink {
/// Create a new `TrapSink`
fn new() -> Self {
Self::default()
}
}
impl binemit::TrapSink for TrapSink {
fn trap(
&mut self,
code_offset: binemit::CodeOffset,
source_loc: ir::SourceLoc,
trap_code: ir::TrapCode,
) {
self.traps.push(TrapInformation {
code_offset,
source_loc,
trap_code,
});
}
}
/// The `FuncEnvironment` implementation for use by the `ModuleEnvironment`.
struct FuncEnvironment<'module_environment> {
/// The module-level environment which this function-level environment belongs to.
module: &'module_environment Module,
/// Offsets to struct fields accessed by JIT code.
offsets: VMOffsets,
}
impl<'module_environment> FuncEnvironment<'module_environment> {
fn new(pointer_bytes: u8, module: &'module_environment Module) -> Self {
Self {
module,
offsets: VMOffsets::new(pointer_bytes, module),
}
}
}
// TODO: This is necessary as if Lightbeam used `FuncEnvironment` directly it would cause
// a circular dependency graph. We should extract common types out into a separate
// crate that Lightbeam can use but until then we need this trait.
impl lightbeam::ModuleContext for FuncEnvironment<'_> {
type Signature = ir::Signature;
type GlobalType = ir::Type;
fn func_index(&self, defined_func_index: u32) -> u32 {
self.module
.func_index(DefinedFuncIndex::from_u32(defined_func_index))
.as_u32()
}
fn defined_func_index(&self, func_index: u32) -> Option<u32> {
self.module
.defined_func_index(FuncIndex::from_u32(func_index))
.map(DefinedFuncIndex::as_u32)
}
fn defined_global_index(&self, global_index: u32) -> Option<u32> {
self.module
.defined_global_index(GlobalIndex::from_u32(global_index))
.map(DefinedGlobalIndex::as_u32)
}
fn global_type(&self, global_index: u32) -> &Self::GlobalType {
&self.module.globals[GlobalIndex::from_u32(global_index)].ty
}
fn func_type_index(&self, func_idx: u32) -> u32 {
self.module.functions[FuncIndex::from_u32(func_idx)].as_u32()
}
fn signature(&self, index: u32) -> &Self::Signature {
&self.module.signatures[SignatureIndex::from_u32(index)].1
}
fn defined_table_index(&self, table_index: u32) -> Option<u32> {
self.module
.defined_table_index(TableIndex::from_u32(table_index))
.map(DefinedTableIndex::as_u32)
}
fn defined_memory_index(&self, memory_index: u32) -> Option<u32> {
self.module
.defined_memory_index(MemoryIndex::from_u32(memory_index))
.map(DefinedMemoryIndex::as_u32)
}
fn vmctx_builtin_function(&self, func_index: u32) -> u32 {
self.offsets
.vmctx_builtin_function(BuiltinFunctionIndex::from_u32(func_index))
}
fn vmctx_vmfunction_import_body(&self, func_index: u32) -> u32 {
self.offsets
.vmctx_vmfunction_import_body(FuncIndex::from_u32(func_index))
}
fn vmctx_vmfunction_import_vmctx(&self, func_index: u32) -> u32 {
self.offsets
.vmctx_vmfunction_import_vmctx(FuncIndex::from_u32(func_index))
}
fn vmctx_vmglobal_import_from(&self, global_index: u32) -> u32 {
self.offsets
.vmctx_vmglobal_import_from(GlobalIndex::from_u32(global_index))
}
fn vmctx_vmglobal_definition(&self, defined_global_index: u32) -> u32 {
self.offsets
.vmctx_vmglobal_definition(DefinedGlobalIndex::from_u32(defined_global_index))
}
fn vmctx_vmmemory_import_from(&self, memory_index: u32) -> u32 {
self.offsets
.vmctx_vmmemory_import_from(MemoryIndex::from_u32(memory_index))
}
fn vmctx_vmmemory_definition(&self, defined_memory_index: u32) -> u32 {
self.offsets
.vmctx_vmmemory_definition(DefinedMemoryIndex::from_u32(defined_memory_index))
}
fn vmctx_vmmemory_definition_base(&self, defined_memory_index: u32) -> u32 {
self.offsets
.vmctx_vmmemory_definition_base(DefinedMemoryIndex::from_u32(defined_memory_index))
}
fn vmctx_vmmemory_definition_current_length(&self, defined_memory_index: u32) -> u32 {
self.offsets
.vmctx_vmmemory_definition_current_length(DefinedMemoryIndex::from_u32(
defined_memory_index,
))
}
fn vmmemory_definition_base(&self) -> u8 {
self.offsets.vmmemory_definition_base()
}
fn vmmemory_definition_current_length(&self) -> u8 {
self.offsets.vmmemory_definition_current_length()
}
fn vmctx_vmtable_import_from(&self, table_index: u32) -> u32 {
self.offsets
.vmctx_vmtable_import_from(TableIndex::from_u32(table_index))
}
fn vmctx_vmtable_definition(&self, defined_table_index: u32) -> u32 {
self.offsets
.vmctx_vmtable_definition(DefinedTableIndex::from_u32(defined_table_index))
}
fn vmctx_vmtable_definition_base(&self, defined_table_index: u32) -> u32 {
self.offsets
.vmctx_vmtable_definition_base(DefinedTableIndex::from_u32(defined_table_index))
}
fn vmctx_vmtable_definition_current_elements(&self, defined_table_index: u32) -> u32 {
self.offsets
.vmctx_vmtable_definition_current_elements(DefinedTableIndex::from_u32(
defined_table_index,
))
}
fn vmtable_definition_base(&self) -> u8 {
self.offsets.vmtable_definition_base()
}
fn vmtable_definition_current_elements(&self) -> u8 {
self.offsets.vmtable_definition_current_elements()
}
fn vmcaller_checked_anyfunc_type_index(&self) -> u8 {
self.offsets.vmcaller_checked_anyfunc_type_index()
}
fn vmcaller_checked_anyfunc_func_ptr(&self) -> u8 {
self.offsets.vmcaller_checked_anyfunc_func_ptr()
}
fn vmcaller_checked_anyfunc_vmctx(&self) -> u8 {
self.offsets.vmcaller_checked_anyfunc_vmctx()
}
fn size_of_vmcaller_checked_anyfunc(&self) -> u8 {
self.offsets.size_of_vmcaller_checked_anyfunc()
}
fn vmctx_vmshared_signature_id(&self, signature_idx: u32) -> u32 {
self.offsets
.vmctx_vmshared_signature_id(SignatureIndex::from_u32(signature_idx))
}
// TODO: type of a global
}

View File

@@ -13,7 +13,7 @@ edition = "2018"
[dependencies]
anyhow = "1.0"
wasmtime-environ = { path = "../environ", version = "0.19.0" }
object = { version = "0.20", default-features = false, features = ["write"] }
object = { version = "0.21.1", default-features = false, features = ["write"] }
more-asserts = "0.2.1"
target-lexicon = { version = "0.10.0", default-features = false }
wasmtime-debug = { path = "../debug", version = "0.19.0" }

View File

@@ -22,7 +22,6 @@ thiserror = "1.0"
libc = "0.2"
getrandom = { version = "0.1.14", features = ["std"] }
cfg-if = "0.1.9"
log = "0.4"
filetime = "0.2.7"
lazy_static = "1.4.0"
wig = { path = "wig", version = "0.19.0" }
@@ -45,7 +44,7 @@ default = ["trace_log"]
# This feature enables the `tracing` logs in the calls to target the `log`
# ecosystem of backends (e.g. `env_logger`. Disable this if you want to use
# `tracing-subscriber`.
trace_log = [ "wiggle/tracing_log" ]
trace_log = [ "wiggle/tracing_log", "tracing/log" ]
# Need to make the wiggle_metadata feature available to consumers of this
# crate if they want the snapshots to have metadata available.
wiggle_metadata = ["wiggle/wiggle_metadata"]

View File

@@ -359,7 +359,10 @@ impl WasiCtxBuilder {
self.stdout.take().unwrap(),
self.stderr.take().unwrap(),
] {
log::debug!("WasiCtx inserting entry {:?}", pending);
tracing::debug!(
pending = tracing::field::debug(&pending),
"WasiCtx inserting entry"
);
let fd = match pending {
PendingEntry::Thunk(f) => {
let handle = EntryHandle::from(f()?);
@@ -376,7 +379,7 @@ impl WasiCtxBuilder {
.ok_or(WasiCtxBuilderError::TooManyFilesOpen)?
}
};
log::debug!("WasiCtx inserted at {:?}", fd);
tracing::debug!(fd = tracing::field::debug(fd), "WasiCtx inserted");
}
// Then add the preopen entries.
for (guest_path, preopen) in self.preopens.take().unwrap() {
@@ -386,7 +389,7 @@ impl WasiCtxBuilder {
let fd = entries
.insert(entry)
.ok_or(WasiCtxBuilderError::TooManyFilesOpen)?;
log::debug!("WasiCtx inserted at {:?}", fd);
tracing::debug!(fd = tracing::field::debug(fd), "WasiCtx inserted",);
}
Ok(WasiCtx {

View File

@@ -18,6 +18,12 @@ impl EntryHandle {
}
}
impl std::fmt::Debug for EntryHandle {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("EntryHandle").field("opaque", &()).finish()
}
}
impl From<Box<dyn Handle>> for EntryHandle {
fn from(handle: Box<dyn Handle>) -> Self {
Self(handle.into())
@@ -87,10 +93,10 @@ impl Entry {
if this_rights.contains(rights) {
Ok(())
} else {
log::trace!(
" | validate_rights failed: required rights = {}; actual rights = {}",
rights,
this_rights,
tracing::trace!(
required = tracing::field::display(rights),
actual = tracing::field::display(this_rights),
"validate_rights failed",
);
Err(Error::Notcapable)
}

View File

@@ -287,7 +287,7 @@ impl WasiCtxBuilder {
let fd = fd_pool
.allocate()
.ok_or(WasiCtxBuilderError::TooManyFilesOpen)?;
log::debug!("WasiCtx inserting ({:?}, {:?})", fd, pending);
tracing::debug!("WasiCtx inserting ({:?}, {:?})", fd, pending);
match pending.take().unwrap() {
PendingEntry::Thunk(f) => {
entries.insert(fd, f()?);
@@ -311,9 +311,9 @@ impl WasiCtxBuilder {
let mut fe = Entry::from(dir)?;
fe.preopen_path = Some(guest_path);
log::debug!("WasiCtx inserting ({:?}, {:?})", preopen_fd, fe);
tracing::debug!("WasiCtx inserting ({:?}, {:?})", preopen_fd, fe);
entries.insert(preopen_fd, fe);
log::debug!("WasiCtx entries = {:?}", entries);
tracing::debug!("WasiCtx entries = {:?}", entries);
}
Ok(WasiCtx {

View File

@@ -11,11 +11,11 @@ use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult};
use crate::old::snapshot_0::{helpers, host, wasi32};
use crate::sandboxed_tty_writer::SandboxedTTYWriter;
use filetime::{set_file_handle_times, FileTime};
use log::trace;
use std::fs::File;
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::ops::DerefMut;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tracing::trace;
pub(crate) unsafe fn fd_close(
wasi_ctx: &mut WasiCtx,
@@ -686,8 +686,8 @@ pub(crate) unsafe fn path_rename(
true,
)?;
log::debug!("path_rename resolved_old={:?}", resolved_old);
log::debug!("path_rename resolved_new={:?}", resolved_new);
tracing::debug!("path_rename resolved_old={:?}", resolved_old);
tracing::debug!("path_rename resolved_new={:?}", resolved_new);
hostcalls_impl::path_rename(resolved_old, resolved_new)
}
@@ -950,7 +950,7 @@ pub(crate) unsafe fn path_remove_directory(
true,
)?;
log::debug!("path_remove_directory resolved={:?}", resolved);
tracing::debug!("path_remove_directory resolved={:?}", resolved);
hostcalls_impl::path_remove_directory(resolved)
}

View File

@@ -68,7 +68,7 @@ pub(crate) fn path_get(
loop {
match path_stack.pop() {
Some(cur_path) => {
log::debug!("path_get cur_path = {:?}", cur_path);
tracing::debug!("path_get cur_path = {:?}", cur_path);
let ends_with_slash = cur_path.ends_with('/');
let mut components = Path::new(&cur_path).components();
@@ -86,7 +86,7 @@ pub(crate) fn path_get(
path_stack.push(tail);
}
log::debug!("path_get path_stack = {:?}", path_stack);
tracing::debug!("path_get path_stack = {:?}", path_stack);
match head {
Component::Prefix(_) | Component::RootDir => {
@@ -140,7 +140,7 @@ pub(crate) fn path_get(
link_path.push('/');
}
log::debug!(
tracing::debug!(
"attempted symlink expansion link_path={:?}",
link_path
);
@@ -172,7 +172,7 @@ pub(crate) fn path_get(
link_path.push('/');
}
log::debug!(
tracing::debug!(
"attempted symlink expansion link_path={:?}",
link_path
);

View File

@@ -5,8 +5,8 @@ use crate::old::snapshot_0::memory::*;
use crate::old::snapshot_0::sys::hostcalls_impl;
use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult};
use crate::old::snapshot_0::wasi32;
use log::{error, trace};
use std::convert::TryFrom;
use tracing::{error, trace};
pub(crate) fn args_get(
wasi_ctx: &WasiCtx,
@@ -227,8 +227,8 @@ pub(crate) fn poll_oneoff(
let clock = unsafe { subscription.u.u.clock };
let delay = wasi_clock_to_relative_ns_delay(clock)?;
log::debug!("poll_oneoff event.u.clock = {:?}", clock);
log::debug!("poll_oneoff delay = {:?}ns", delay);
tracing::debug!("poll_oneoff event.u.clock = {:?}", clock);
tracing::debug!("poll_oneoff delay = {:?}ns", delay);
let current = ClockEventData {
delay,
@@ -299,8 +299,8 @@ pub(crate) fn poll_oneoff(
}
}
log::debug!("poll_oneoff timeout = {:?}", timeout);
log::debug!("poll_oneoff fd_events = {:?}", fd_events);
tracing::debug!("poll_oneoff timeout = {:?}", timeout);
tracing::debug!("poll_oneoff fd_events = {:?}", fd_events);
hostcalls_impl::poll_oneoff(timeout, fd_events, &mut events)?;

View File

@@ -36,7 +36,7 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> WasiResult<()> {
}
}
Err(err) => {
log::debug!("path_unlink_file fstatat error: {:?}", err);
tracing::debug!("path_unlink_file fstatat error: {:?}", err);
}
}
}
@@ -50,8 +50,8 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> WasiResult<()> {
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> WasiResult<()> {
use yanix::file::{fstatat, symlinkat, AtFlags};
log::debug!("path_symlink old_path = {:?}", old_path);
log::debug!("path_symlink resolved = {:?}", resolved);
tracing::debug!("path_symlink old_path = {:?}", old_path);
tracing::debug!("path_symlink resolved = {:?}", resolved);
match unsafe { symlinkat(old_path, resolved.dirfd().as_raw_fd(), resolved.path()) } {
Err(err) => {
@@ -71,7 +71,7 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> WasiResult<()>
} {
Ok(_) => return Err(WasiError::EEXIST),
Err(err) => {
log::debug!("path_symlink fstatat error: {:?}", err);
tracing::debug!("path_symlink fstatat error: {:?}", err);
}
}
}
@@ -119,7 +119,7 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> WasiR
}
}
Err(err) => {
log::debug!("path_rename fstatat error: {:?}", err);
tracing::debug!("path_rename fstatat error: {:?}", err);
}
}
}

View File

@@ -61,14 +61,14 @@ pub(crate) unsafe fn determine_type_rights<Fd: AsRawFd>(
let file = std::mem::ManuallyDrop::new(std::fs::File::from_raw_fd(fd.as_raw_fd()));
let ft = file.metadata()?.file_type();
if ft.is_block_device() {
log::debug!("Host fd {:?} is a block device", fd.as_raw_fd());
tracing::debug!("Host fd {:?} is a block device", fd.as_raw_fd());
(
wasi::__WASI_FILETYPE_BLOCK_DEVICE,
wasi::RIGHTS_BLOCK_DEVICE_BASE,
wasi::RIGHTS_BLOCK_DEVICE_INHERITING,
)
} else if ft.is_char_device() {
log::debug!("Host fd {:?} is a char device", fd.as_raw_fd());
tracing::debug!("Host fd {:?} is a char device", fd.as_raw_fd());
use yanix::file::isatty;
if isatty(fd.as_raw_fd())? {
(
@@ -84,21 +84,21 @@ pub(crate) unsafe fn determine_type_rights<Fd: AsRawFd>(
)
}
} else if ft.is_dir() {
log::debug!("Host fd {:?} is a directory", fd.as_raw_fd());
tracing::debug!("Host fd {:?} is a directory", fd.as_raw_fd());
(
wasi::__WASI_FILETYPE_DIRECTORY,
wasi::RIGHTS_DIRECTORY_BASE,
wasi::RIGHTS_DIRECTORY_INHERITING,
)
} else if ft.is_file() {
log::debug!("Host fd {:?} is a file", fd.as_raw_fd());
tracing::debug!("Host fd {:?} is a file", fd.as_raw_fd());
(
wasi::__WASI_FILETYPE_REGULAR_FILE,
wasi::RIGHTS_REGULAR_FILE_BASE,
wasi::RIGHTS_REGULAR_FILE_INHERITING,
)
} else if ft.is_socket() {
log::debug!("Host fd {:?} is a socket", fd.as_raw_fd());
tracing::debug!("Host fd {:?} is a socket", fd.as_raw_fd());
use yanix::socket::{get_socket_type, SockType};
match get_socket_type(fd.as_raw_fd())? {
SockType::Datagram => (
@@ -114,14 +114,14 @@ pub(crate) unsafe fn determine_type_rights<Fd: AsRawFd>(
_ => return Err(io::Error::from_raw_os_error(libc::EINVAL)),
}
} else if ft.is_fifo() {
log::debug!("Host fd {:?} is a fifo", fd.as_raw_fd());
tracing::debug!("Host fd {:?} is a fifo", fd.as_raw_fd());
(
wasi::__WASI_FILETYPE_UNKNOWN,
wasi::RIGHTS_REGULAR_FILE_BASE,
wasi::RIGHTS_REGULAR_FILE_INHERITING,
)
} else {
log::debug!("Host fd {:?} is unknown", fd.as_raw_fd());
tracing::debug!("Host fd {:?} is unknown", fd.as_raw_fd());
return Err(io::Error::from_raw_os_error(libc::EINVAL));
}
};

View File

@@ -92,12 +92,12 @@ impl From<io::Error> for WasiError {
libc::ENOTRECOVERABLE => Self::ENOTRECOVERABLE,
libc::ENOTSUP => Self::ENOTSUP,
x => {
log::debug!("Unknown errno value: {}", x);
tracing::debug!("Unknown errno value: {}", x);
Self::EIO
}
},
None => {
log::debug!("Other I/O error: {}", err);
tracing::debug!("Other I/O error: {}", err);
Self::EIO
}
}

View File

@@ -115,8 +115,8 @@ pub(crate) fn path_open(
// umask is, but don't set the executable flag, because it isn't yet
// meaningful for WASI programs to create executable files.
log::debug!("path_open resolved = {:?}", resolved);
log::debug!("path_open oflags = {:?}", nix_all_oflags);
tracing::debug!("path_open resolved = {:?}", resolved);
tracing::debug!("path_open oflags = {:?}", nix_all_oflags);
let new_fd = match unsafe {
openat(
@@ -144,7 +144,7 @@ pub(crate) fn path_open(
}
}
Err(err) => {
log::debug!("path_open fstatat error: {:?}", err);
tracing::debug!("path_open fstatat error: {:?}", err);
}
}
}
@@ -166,7 +166,7 @@ pub(crate) fn path_open(
}
}
Err(err) => {
log::debug!("path_open fstatat error: {:?}", err);
tracing::debug!("path_open fstatat error: {:?}", err);
}
}
}
@@ -182,7 +182,7 @@ pub(crate) fn path_open(
}
};
log::debug!("path_open (host) new_fd = {:?}", new_fd);
tracing::debug!("path_open (host) new_fd = {:?}", new_fd);
// Determine the type of the new file descriptor and which rights contradict with this type
Ok(unsafe { File::from_raw_fd(new_fd) })
@@ -294,10 +294,10 @@ pub(crate) fn fd_readdir<'a>(
// Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START,
// new items may not be returned to the caller.
if cookie == wasi::__WASI_DIRCOOKIE_START {
log::trace!(" | fd_readdir: doing rewinddir");
tracing::trace!(" | fd_readdir: doing rewinddir");
dir.rewind();
} else {
log::trace!(" | fd_readdir: doing seekdir to {}", cookie);
tracing::trace!(" | fd_readdir: doing seekdir to {}", cookie);
let loc = unsafe { SeekLoc::from_raw(cookie as i64)? };
dir.seek(loc);
}

View File

@@ -40,7 +40,7 @@ pub(crate) fn openat(dirfd: &File, path: &str) -> WasiResult<File> {
use std::os::unix::prelude::{AsRawFd, FromRawFd};
use yanix::file::{openat, Mode};
log::debug!("path_get openat path = {:?}", path);
tracing::debug!("path_get openat path = {:?}", path);
unsafe {
openat(
@@ -58,7 +58,7 @@ pub(crate) fn readlinkat(dirfd: &File, path: &str) -> WasiResult<String> {
use std::os::unix::prelude::AsRawFd;
use yanix::file::readlinkat;
log::debug!("path_get readlinkat path = {:?}", path);
tracing::debug!("path_get readlinkat path = {:?}", path);
unsafe { readlinkat(dirfd.as_raw_fd(), path) }
.map_err(Into::into)

View File

@@ -85,7 +85,7 @@ pub(crate) fn poll_oneoff(
let delay = timeout.delay / 1_000_000; // poll syscall requires delay to expressed in milliseconds
delay.try_into().unwrap_or(libc::c_int::max_value())
});
log::debug!("poll_oneoff poll_timeout = {:?}", poll_timeout);
tracing::debug!("poll_oneoff poll_timeout = {:?}", poll_timeout);
let ready = loop {
match poll(&mut poll_fds, poll_timeout) {
@@ -131,15 +131,15 @@ fn poll_oneoff_handle_fd_event<'a>(
use yanix::{file::fionread, poll::PollFlags};
for (fd_event, poll_fd) in ready_events {
log::debug!("poll_oneoff_handle_fd_event fd_event = {:?}", fd_event);
log::debug!("poll_oneoff_handle_fd_event poll_fd = {:?}", poll_fd);
tracing::debug!("poll_oneoff_handle_fd_event fd_event = {:?}", fd_event);
tracing::debug!("poll_oneoff_handle_fd_event poll_fd = {:?}", poll_fd);
let revents = match poll_fd.revents() {
Some(revents) => revents,
None => continue,
};
log::debug!("poll_oneoff_handle_fd_event revents = {:?}", revents);
tracing::debug!("poll_oneoff_handle_fd_event revents = {:?}", revents);
let nbytes = if fd_event.r#type == wasi::__WASI_EVENTTYPE_FD_READ {
unsafe { fionread(fd_event.descriptor.as_raw_fd())? }

View File

@@ -17,8 +17,8 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> WasiResult<()> {
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> WasiResult<()> {
use yanix::file::symlinkat;
log::debug!("path_symlink old_path = {:?}", old_path);
log::debug!("path_symlink resolved = {:?}", resolved);
tracing::debug!("path_symlink old_path = {:?}", old_path);
tracing::debug!("path_symlink resolved = {:?}", resolved);
unsafe { symlinkat(old_path, resolved.dirfd().as_raw_fd(), resolved.path()) }
.map_err(Into::into)

View File

@@ -43,12 +43,12 @@ impl From<io::Error> for WasiError {
winerror::ERROR_DIRECTORY => Self::ENOTDIR,
winerror::ERROR_ALREADY_EXISTS => Self::EEXIST,
x => {
log::debug!("unknown error value: {}", x);
tracing::debug!("unknown error value: {}", x);
Self::EIO
}
},
None => {
log::debug!("Other I/O error: {}", err);
tracing::debug!("Other I/O error: {}", err);
Self::EIO
}
}

View File

@@ -9,13 +9,13 @@ use crate::old::snapshot_0::sys::entry_impl::determine_type_rights;
use crate::old::snapshot_0::sys::host_impl::{self, path_from_host};
use crate::old::snapshot_0::sys::hostcalls_impl::fs_helpers::PathGetExt;
use crate::old::snapshot_0::wasi::{self, WasiError, WasiResult};
use log::{debug, trace};
use std::convert::TryInto;
use std::fs::{File, Metadata, OpenOptions};
use std::io::{self, Seek, SeekFrom};
use std::os::windows::fs::{FileExt, OpenOptionsExt};
use std::os::windows::prelude::{AsRawHandle, FromRawHandle};
use std::path::{Path, PathBuf};
use tracing::{debug, trace};
use winapi::shared::winerror;
use winx::file::{AccessMode, CreationDisposition, FileModeInformation, Flags};
@@ -156,7 +156,7 @@ pub(crate) fn path_open(
}
Err(err) => match err.raw_os_error() {
Some(code) => {
log::debug!("path_open at symlink_metadata error code={:?}", code);
tracing::debug!("path_open at symlink_metadata error code={:?}", code);
if code as u32 != winerror::ERROR_FILE_NOT_FOUND {
return Err(err.into());
@@ -165,7 +165,7 @@ pub(crate) fn path_open(
// trying to open it
}
None => {
log::debug!("Inconvertible OS error: {}", err);
tracing::debug!("Inconvertible OS error: {}", err);
return Err(WasiError::EIO);
}
},
@@ -397,7 +397,7 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> WasiR
};
match err.raw_os_error() {
Some(code) => {
log::debug!("path_rename at rename error code={:?}", code);
tracing::debug!("path_rename at rename error code={:?}", code);
match code as u32 {
winerror::ERROR_ACCESS_DENIED => {
// So most likely dealing with new_path == dir.
@@ -427,7 +427,7 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> WasiR
Err(err.into())
}
None => {
log::debug!("Inconvertible OS error: {}", err);
tracing::debug!("Inconvertible OS error: {}", err);
Err(WasiError::EIO)
}
}
@@ -474,7 +474,7 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> WasiResult<()>
};
match err.raw_os_error() {
Some(code) => {
log::debug!("path_symlink at symlink_file error code={:?}", code);
tracing::debug!("path_symlink at symlink_file error code={:?}", code);
match code as u32 {
winerror::ERROR_NOT_A_REPARSE_POINT => {
// try creating a dir symlink instead
@@ -500,7 +500,7 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> WasiResult<()>
Err(err.into())
}
None => {
log::debug!("Inconvertible OS error: {}", err);
tracing::debug!("Inconvertible OS error: {}", err);
Err(WasiError::EIO)
}
}
@@ -526,7 +526,7 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> WasiResult<()> {
};
match err.raw_os_error() {
Some(code) => {
log::debug!("path_unlink_file at symlink_file error code={:?}", code);
tracing::debug!("path_unlink_file at symlink_file error code={:?}", code);
if code as u32 == winerror::ERROR_ACCESS_DENIED {
// try unlinking a dir symlink instead
return fs::remove_dir(path).map_err(Into::into);
@@ -535,7 +535,7 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> WasiResult<()> {
Err(err.into())
}
None => {
log::debug!("Inconvertible OS error: {}", err);
tracing::debug!("Inconvertible OS error: {}", err);
Err(WasiError::EIO)
}
}

View File

@@ -61,7 +61,7 @@ pub(crate) fn openat(dirfd: &File, path: &str) -> WasiResult<File> {
Err(e) => e,
};
if let Some(code) = err.raw_os_error() {
log::debug!("openat error={:?}", code);
tracing::debug!("openat error={:?}", code);
if code as u32 == winerror::ERROR_INVALID_NAME {
return Err(WasiError::ENOTDIR);
}
@@ -90,7 +90,7 @@ pub(crate) fn readlinkat(dirfd: &File, s_path: &str) -> WasiResult<String> {
Err(e) => e,
};
if let Some(code) = err.raw_os_error() {
log::debug!("readlinkat error={:?}", code);
tracing::debug!("readlinkat error={:?}", code);
if code as u32 == winerror::ERROR_INVALID_NAME {
if s_path.ends_with('/') {
// strip "/" and check if exists
@@ -130,7 +130,7 @@ pub(crate) fn concatenate<P: AsRef<Path>>(dirfd: &File, path: P) -> WasiResult<P
// components with `out_path`
let out_path = PathBuf::from(strip_extended_prefix(out_path));
log::debug!("out_path={:?}", out_path);
tracing::debug!("out_path={:?}", out_path);
Ok(out_path)
}

View File

@@ -23,7 +23,7 @@ pub(crate) fn get(
// Extract path as &str from guest's memory.
let path = path_ptr.as_str()?;
log::trace!(" | (path_ptr,path_len)='{}'", &*path);
tracing::trace!(path = &*path);
if path.contains('\0') {
// if contains NUL, return Ilseq
@@ -56,7 +56,7 @@ pub(crate) fn get(
loop {
match path_stack.pop() {
Some(cur_path) => {
log::debug!("path_get cur_path = {:?}", cur_path);
tracing::debug!(cur_path = tracing::field::display(&cur_path), "path get");
let ends_with_slash = cur_path.ends_with('/');
let mut components = Path::new(&cur_path).components();
@@ -74,7 +74,7 @@ pub(crate) fn get(
path_stack.push(tail);
}
log::debug!("path_get path_stack = {:?}", path_stack);
tracing::debug!(path_stack = tracing::field::debug(&path_stack), "path_get");
match head {
Component::Prefix(_) | Component::RootDir => {
@@ -130,7 +130,7 @@ pub(crate) fn get(
link_path.push('/');
}
log::debug!(
tracing::debug!(
"attempted symlink expansion link_path={:?}",
link_path
);
@@ -162,7 +162,7 @@ pub(crate) fn get(
link_path.push('/');
}
log::debug!(
tracing::debug!(
"attempted symlink expansion link_path={:?}",
link_path
);

View File

@@ -9,6 +9,7 @@ pub(crate) struct ClockEventData {
pub(crate) userdata: types::Userdata,
}
#[derive(Debug)]
pub(crate) struct FdEventData {
pub(crate) handle: EntryHandle,
pub(crate) r#type: types::Eventtype,

View File

@@ -3,11 +3,10 @@ use crate::handle::HandleRights;
use crate::sys::clock;
use crate::wasi::wasi_snapshot_preview1::WasiSnapshotPreview1;
use crate::wasi::{types, AsBytes};
use crate::{path, poll};
use crate::{Error, Result, WasiCtx};
use log::{debug, trace};
use crate::{path, poll, Error, Result, WasiCtx};
use std::convert::TryInto;
use std::io::{self, SeekFrom};
use tracing::{debug, trace};
use wiggle::{GuestPtr, GuestSlice};
impl<'a> WasiSnapshotPreview1 for WasiCtx {
@@ -651,7 +650,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
true,
)?;
let old_path = old_path.as_str()?;
trace!(" | old_path='{}'", &*old_path);
trace!(old_path = &*old_path);
new_fd.symlink(&old_path, &new_path)
}
@@ -701,8 +700,11 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
match subscription.u {
types::SubscriptionU::Clock(clock) => {
let delay = clock::to_relative_ns_delay(&clock)?;
debug!("poll_oneoff event.u.clock = {:?}", clock);
debug!("poll_oneoff delay = {:?}ns", delay);
debug!(
clock = tracing::field::debug(&clock),
delay_ns = tracing::field::debug(delay),
"poll_oneoff"
);
let current = poll::ClockEventData {
delay,
userdata: subscription.userdata,
@@ -766,8 +768,11 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
}
}
}
debug!("poll_oneoff events = {:?}", events);
debug!("poll_oneoff timeout = {:?}", timeout);
debug!(
events = tracing::field::debug(&events),
timeout = tracing::field::debug(timeout),
"poll_oneoff"
);
// The underlying implementation should successfully and immediately return
// if no events have been passed. Such situation may occur if all provided
// events have been filtered out as errors in the code above.
@@ -780,7 +785,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
event_ptr.write(event)?;
}
trace!(" | *nevents={:?}", nevents);
trace!(nevents = nevents);
Ok(nevents)
}

View File

@@ -55,7 +55,7 @@ impl AsFile for dyn Handle + 'static {
} else if let Some(other) = self.as_any().downcast_ref::<OsOther>() {
other.as_file()
} else {
log::error!("tried to make std::fs::File from non-OS handle");
tracing::error!("tried to make std::fs::File from non-OS handle");
Err(io::Error::from_raw_os_error(libc::EBADF))
}
}
@@ -69,17 +69,26 @@ impl TryFrom<File> for Box<dyn Handle> {
match file_type {
types::Filetype::RegularFile => {
let handle = OsFile::try_from(file)?;
log::debug!("Created new instance of OsFile: {:?}", handle);
tracing::debug!(
handle = tracing::field::debug(&handle),
"Created new instance of OsFile"
);
Ok(Box::new(handle))
}
types::Filetype::Directory => {
let handle = OsDir::try_from(file)?;
log::debug!("Created new instance of OsDir: {:?}", handle);
tracing::debug!(
handle = tracing::field::debug(&handle),
"Created new instance of OsDir"
);
Ok(Box::new(handle))
}
_ => {
let handle = OsOther::try_from(file)?;
log::debug!("Created new instance of OsOther: {:?}", handle);
tracing::debug!(
handle = tracing::field::debug(&handle),
"Created new instance of OsOther"
);
Ok(Box::new(handle))
}
}

View File

@@ -3,10 +3,10 @@ use super::{fd, path, AsFile};
use crate::handle::{Handle, HandleRights};
use crate::wasi::types;
use crate::{Error, Result};
use log::{debug, error};
use std::any::Any;
use std::io;
use std::ops::Deref;
use tracing::{debug, error};
// TODO could this be cleaned up?
// The actual `OsDir` struct is OS-dependent, therefore we delegate

View File

@@ -24,7 +24,7 @@ pub(crate) fn unlink_file(dirfd: &OsDir, path: &str) -> Result<()> {
}
}
Err(err) => {
log::debug!("path_unlink_file fstatat error: {:?}", err);
tracing::debug!("path_unlink_file fstatat error: {:?}", err);
}
}
}
@@ -38,8 +38,8 @@ pub(crate) fn unlink_file(dirfd: &OsDir, path: &str) -> Result<()> {
pub(crate) fn symlink(old_path: &str, new_dirfd: &OsDir, new_path: &str) -> Result<()> {
use yanix::file::{fstatat, symlinkat, AtFlags};
log::debug!("path_symlink old_path = {:?}", old_path);
log::debug!(
tracing::debug!("path_symlink old_path = {:?}", old_path);
tracing::debug!(
"path_symlink (new_dirfd, new_path) = ({:?}, {:?})",
new_dirfd,
new_path
@@ -58,7 +58,7 @@ pub(crate) fn symlink(old_path: &str, new_dirfd: &OsDir, new_path: &str) -> Resu
{
Ok(_) => return Err(Error::Exist),
Err(err) => {
log::debug!("path_symlink fstatat error: {:?}", err);
tracing::debug!("path_symlink fstatat error: {:?}", err);
}
}
}
@@ -106,7 +106,7 @@ pub(crate) fn rename(
}
}
Err(err) => {
log::debug!("path_rename fstatat error: {:?}", err);
tracing::debug!("path_rename fstatat error: {:?}", err);
}
}
}

View File

@@ -60,10 +60,10 @@ pub(crate) fn readdir<'a>(
// Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START,
// new items may not be returned to the caller.
if cookie == wasi::DIRCOOKIE_START {
log::trace!(" | fd_readdir: doing rewinddir");
tracing::trace!("fd_readdir: doing rewinddir");
dir.rewind();
} else {
log::trace!(" | fd_readdir: doing seekdir to {}", cookie);
tracing::trace!("fd_readdir: doing seekdir to {}", cookie);
let loc = unsafe { SeekLoc::from_raw(cookie as i64)? };
dir.seek(loc);
}

View File

@@ -11,11 +11,11 @@ pub(crate) fn unlink_file(dirfd: &OsDir, path: &str) -> Result<()> {
pub(crate) fn symlink(old_path: &str, new_dirfd: &OsDir, new_path: &str) -> Result<()> {
use yanix::file::symlinkat;
log::debug!("path_symlink old_path = {:?}", old_path);
log::debug!(
"path_symlink (new_dirfd, new_path) = ({:?}, {:?})",
new_dirfd,
new_path
tracing::debug!(
old_path = old_path,
new_dirfd = tracing::field::debug(new_dirfd),
new_path = new_path,
"path symlink"
);
unsafe { symlinkat(old_path, new_dirfd.as_raw_fd(), new_path)? };

View File

@@ -51,19 +51,22 @@ impl<T: AsRawFd> AsFile for T {
pub(super) fn get_file_type(file: &File) -> io::Result<types::Filetype> {
let ft = file.metadata()?.file_type();
let file_type = if ft.is_block_device() {
log::debug!("Host fd {:?} is a block device", file.as_raw_fd());
tracing::debug!(
host_fd = tracing::field::display(file.as_raw_fd()),
"Host fd is a block device"
);
types::Filetype::BlockDevice
} else if ft.is_char_device() {
log::debug!("Host fd {:?} is a char device", file.as_raw_fd());
tracing::debug!("Host fd {:?} is a char device", file.as_raw_fd());
types::Filetype::CharacterDevice
} else if ft.is_dir() {
log::debug!("Host fd {:?} is a directory", file.as_raw_fd());
tracing::debug!("Host fd {:?} is a directory", file.as_raw_fd());
types::Filetype::Directory
} else if ft.is_file() {
log::debug!("Host fd {:?} is a file", file.as_raw_fd());
tracing::debug!("Host fd {:?} is a file", file.as_raw_fd());
types::Filetype::RegularFile
} else if ft.is_socket() {
log::debug!("Host fd {:?} is a socket", file.as_raw_fd());
tracing::debug!("Host fd {:?} is a socket", file.as_raw_fd());
use yanix::socket::{get_socket_type, SockType};
match unsafe { get_socket_type(file.as_raw_fd())? } {
SockType::Datagram => types::Filetype::SocketDgram,
@@ -71,10 +74,10 @@ pub(super) fn get_file_type(file: &File) -> io::Result<types::Filetype> {
_ => return Err(io::Error::from_raw_os_error(libc::EINVAL)),
}
} else if ft.is_fifo() {
log::debug!("Host fd {:?} is a fifo", file.as_raw_fd());
tracing::debug!("Host fd {:?} is a fifo", file.as_raw_fd());
types::Filetype::Unknown
} else {
log::debug!("Host fd {:?} is unknown", file.as_raw_fd());
tracing::debug!("Host fd {:?} is unknown", file.as_raw_fd());
return Err(io::Error::from_raw_os_error(libc::EINVAL));
};
Ok(file_type)

View File

@@ -55,7 +55,7 @@ pub(crate) fn readlinkat(dirfd: &OsDir, path: &str) -> Result<String> {
use std::os::unix::prelude::AsRawFd;
use yanix::file::readlinkat;
log::debug!("path_get readlinkat path = {:?}", path);
tracing::debug!(path = path, "path_get readlinkat");
let path = unsafe { readlinkat(dirfd.as_raw_fd(), path)? };
let path = from_host(path)?;
@@ -124,9 +124,12 @@ pub(crate) fn open(
// umask is, but don't set the executable flag, because it isn't yet
// meaningful for WASI programs to create executable files.
log::debug!("path_open dirfd = {:?}", dirfd);
log::debug!("path_open path = {:?}", path);
log::debug!("path_open oflags = {:?}", nix_all_oflags);
tracing::debug!(
dirfd = tracing::field::debug(dirfd),
path = tracing::field::debug(path),
oflags = tracing::field::debug(nix_all_oflags),
"path_open"
);
let fd_no = unsafe {
openat(
@@ -149,7 +152,10 @@ pub(crate) fn open(
}
}
Err(err) => {
log::debug!("path_open fstatat error: {:?}", err);
tracing::debug!(
error = tracing::field::debug(&err),
"path_open fstatat error",
);
}
}
}
@@ -165,7 +171,10 @@ pub(crate) fn open(
}
}
Err(err) => {
log::debug!("path_open fstatat error: {:?}", err);
tracing::debug!(
error = tracing::field::debug(&err),
"path_open fstatat error",
);
}
}
}
@@ -181,7 +190,7 @@ pub(crate) fn open(
}
};
log::debug!("path_open (host) new_fd = {:?}", new_fd);
tracing::debug!(new_fd = tracing::field::debug(new_fd));
// Determine the type of the new file descriptor and which rights contradict with this type
let file = unsafe { File::from_raw_fd(new_fd) };

View File

@@ -39,7 +39,10 @@ pub(crate) fn oneoff(
let delay = timeout.delay / 1_000_000; // poll syscall requires delay to expressed in milliseconds
delay.try_into().unwrap_or(libc::c_int::max_value())
});
log::debug!("poll_oneoff poll_timeout = {:?}", poll_timeout);
tracing::debug!(
poll_timeout = tracing::field::debug(poll_timeout),
"poll_oneoff"
);
let ready = loop {
match poll(&mut poll_fds, poll_timeout) {
@@ -92,16 +95,17 @@ fn handle_fd_event(
}
for (fd_event, poll_fd) in ready_events {
// log::debug!("poll_oneoff_handle_fd_event fd_event = {:?}", fd_event);
log::debug!("poll_oneoff_handle_fd_event poll_fd = {:?}", poll_fd);
tracing::debug!(
poll_fd = tracing::field::debug(poll_fd),
poll_event = tracing::field::debug(&fd_event),
"poll_oneoff handle_fd_event"
);
let revents = match poll_fd.revents() {
Some(revents) => revents,
None => continue,
};
log::debug!("poll_oneoff_handle_fd_event revents = {:?}", revents);
let nbytes = if fd_event.r#type == types::Eventtype::FdRead {
query_nbytes(fd_event.handle)?
} else {

View File

@@ -6,12 +6,12 @@ use crate::sys::osfile::OsFile;
use crate::sys::AsFile;
use crate::wasi::types;
use crate::Result;
use log::trace;
use std::convert::TryInto;
use std::fs::{File, OpenOptions};
use std::os::windows::fs::OpenOptionsExt;
use std::os::windows::prelude::{AsRawHandle, FromRawHandle};
use std::path::Path;
use tracing::trace;
use winx::file::{AccessMode, FileModeInformation, Flags};
pub(crate) fn fdstat_get(file: &File) -> Result<types::Fdflags> {

View File

@@ -47,7 +47,7 @@ fn concatenate<P: AsRef<Path>>(file: &OsDir, path: P) -> Result<PathBuf> {
// components with `out_path`
let out_path = PathBuf::from(strip_extended_prefix(out_path));
log::debug!("out_path={:?}", out_path);
tracing::debug!(out_path = tracing::field::debug(&out_path));
Ok(out_path)
}
@@ -139,7 +139,7 @@ pub(crate) fn readlinkat(dirfd: &OsDir, s_path: &str) -> Result<String> {
Err(e) => e,
};
if let Some(code) = err.raw_os_error() {
log::debug!("readlinkat error={:?}", code);
tracing::debug!("readlinkat error={:?}", code);
if code as u32 == winerror::ERROR_INVALID_NAME {
if s_path.ends_with('/') {
// strip "/" and check if exists
@@ -171,7 +171,10 @@ pub(crate) fn link(
let new_path = concatenate(new_dirfd, new_path)?;
if follow_symlinks {
// in particular, this will return an error if the target path doesn't exist
log::debug!("Following symlinks for path: {:?}", old_path);
tracing::debug!(
old_path = tracing::field::display(old_path.display()),
"Following symlinks"
);
old_path = fs::canonicalize(&old_path).map_err(|e| match e.raw_os_error() {
// fs::canonicalize under Windows will return:
// * ERROR_FILE_NOT_FOUND, if it encounters a dangling symlink
@@ -185,7 +188,7 @@ pub(crate) fn link(
Err(e) => e,
};
if let Some(code) = err.raw_os_error() {
log::debug!("path_link at fs::hard_link error code={:?}", code);
tracing::debug!("path_link at fs::hard_link error code={:?}", code);
if code as u32 == winerror::ERROR_ACCESS_DENIED {
// If an attempt is made to create a hard link to a directory, POSIX-compliant
// implementations of link return `EPERM`, but `ERROR_ACCESS_DENIED` is converted
@@ -249,7 +252,7 @@ pub(crate) fn open(
}
Err(err) => match err.raw_os_error() {
Some(code) => {
log::debug!("path_open at symlink_metadata error code={:?}", code);
tracing::debug!("path_open at symlink_metadata error code={:?}", code);
match code as u32 {
winerror::ERROR_FILE_NOT_FOUND => {
// file not found, let it proceed to actually
@@ -264,7 +267,7 @@ pub(crate) fn open(
};
}
None => {
log::debug!("Inconvertible OS error: {}", err);
tracing::debug!("Inconvertible OS error: {}", err);
return Err(Error::Io);
}
},
@@ -354,7 +357,7 @@ pub(crate) fn rename(
};
match err.raw_os_error() {
Some(code) => {
log::debug!("path_rename at rename error code={:?}", code);
tracing::debug!("path_rename at rename error code={:?}", code);
match code as u32 {
winerror::ERROR_ACCESS_DENIED => {
// So most likely dealing with new_path == dir.
@@ -386,7 +389,7 @@ pub(crate) fn rename(
Err(err.into())
}
None => {
log::debug!("Inconvertible OS error: {}", err);
tracing::debug!("Inconvertible OS error: {}", err);
Err(Error::Io)
}
}
@@ -418,7 +421,7 @@ pub(crate) fn symlink(old_path: &str, new_dirfd: &OsDir, new_path_: &str) -> Res
};
match err.raw_os_error() {
Some(code) => {
log::debug!("path_symlink at symlink_file error code={:?}", code);
tracing::debug!("path_symlink at symlink_file error code={:?}", code);
match code as u32 {
// If the target contains a trailing slash, the Windows API returns
// ERROR_INVALID_NAME (which corresponds to ENOENT) instead of
@@ -443,7 +446,7 @@ pub(crate) fn symlink(old_path: &str, new_dirfd: &OsDir, new_path_: &str) -> Res
Err(err.into())
}
None => {
log::debug!("Inconvertible OS error: {}", err);
tracing::debug!("Inconvertible OS error: {}", err);
Err(Error::Io)
}
}
@@ -469,7 +472,7 @@ pub(crate) fn unlink_file(dirfd: &OsDir, path: &str) -> Result<()> {
};
match err.raw_os_error() {
Some(code) => {
log::debug!("path_unlink_file at symlink_file error code={:?}", code);
tracing::debug!("path_unlink_file at symlink_file error code={:?}", code);
if code as u32 == winerror::ERROR_ACCESS_DENIED {
// try unlinking a dir symlink instead
return fs::remove_dir(path).map_err(Into::into);
@@ -478,7 +481,7 @@ pub(crate) fn unlink_file(dirfd: &OsDir, path: &str) -> Result<()> {
Err(err.into())
}
None => {
log::debug!("Inconvertible OS error: {}", err);
tracing::debug!("Inconvertible OS error: {}", err);
Err(Error::Io)
}
}

View File

@@ -8,12 +8,12 @@ use crate::sys::AsFile;
use crate::wasi::types;
use crate::{Error, Result};
use lazy_static::lazy_static;
use log::{debug, error, trace, warn};
use std::convert::TryInto;
use std::sync::mpsc::{self, Receiver, RecvTimeoutError, Sender, TryRecvError};
use std::sync::Mutex;
use std::thread;
use std::time::Duration;
use tracing::{debug, error, trace, warn};
struct StdinPoll {
request_tx: Sender<()>,
@@ -245,7 +245,7 @@ pub(crate) fn oneoff(
handle_error_event(event, types::Errno::Notsup, events);
}
} else {
log::error!("can poll FdEvent for OS resources only");
tracing::error!("can poll FdEvent for OS resources only");
return Err(Error::Badf);
}
}

View File

@@ -1,7 +1,6 @@
use crate::handle::{Handle, HandleRights};
use crate::wasi::{self, types, RightsExt};
use crate::{Error, Result};
use log::trace;
use std::any::Any;
use std::cell::{Cell, RefCell};
use std::collections::hash_map::Entry;
@@ -11,6 +10,7 @@ use std::io;
use std::io::SeekFrom;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use tracing::trace;
pub mod pipe;
@@ -97,7 +97,7 @@ impl FileContents for VecFileContents {
}
fn pread(&self, buf: &mut [u8], offset: types::Filesize) -> Result<usize> {
trace!(" | pread(buf.len={}, offset={})", buf.len(), offset);
trace!(buffer_length = buf.len(), offset = offset, "pread");
let offset: usize = offset.try_into().map_err(|_| Error::Inval)?;
let data_remaining = self.content.len().saturating_sub(offset);
@@ -106,9 +106,7 @@ impl FileContents for VecFileContents {
(&mut buf[..read_count]).copy_from_slice(&self.content[offset..][..read_count]);
let res = Ok(read_count);
trace!(" | pread={:?}", res);
res
Ok(read_count)
}
fn pwrite(&mut self, buf: &[u8], offset: types::Filesize) -> Result<usize> {
@@ -348,26 +346,16 @@ impl Handle for InMemoryFile {
fn openat(
&self,
path: &str,
read: bool,
write: bool,
_read: bool,
_write: bool,
oflags: types::Oflags,
fd_flags: types::Fdflags,
_fd_flags: types::Fdflags,
) -> Result<Box<dyn Handle>> {
log::trace!(
"InMemoryFile::openat(path={:?}, read={:?}, write={:?}, oflags={:?}, fd_flags={:?}",
path,
read,
write,
oflags,
fd_flags
);
if oflags.contains(&types::Oflags::DIRECTORY) {
log::trace!(
tracing::trace!(
"InMemoryFile::openat was passed oflags DIRECTORY, but {:?} is a file.",
path
);
log::trace!(" return Notdir");
return Err(Error::Notdir);
}
@@ -527,7 +515,7 @@ impl Handle for VirtualDir {
type Item = Result<(types::Dirent, String)>;
fn next(&mut self) -> Option<Self::Item> {
log::trace!("VirtualDirIter::next continuing from {}", self.start);
tracing::trace!("VirtualDirIter::next continuing from {}", self.start);
if self.start == SELF_DIR_COOKIE {
self.start += 1;
let name = ".".to_owned();
@@ -646,20 +634,11 @@ impl Handle for VirtualDir {
fn openat(
&self,
path: &str,
read: bool,
write: bool,
_read: bool,
_write: bool,
oflags: types::Oflags,
fd_flags: types::Fdflags,
) -> Result<Box<dyn Handle>> {
log::trace!(
"VirtualDir::openat(path={:?}, read={:?}, write={:?}, oflags={:?}, fd_flags={:?}",
path,
read,
write,
oflags,
fd_flags
);
if path == "." {
return self.try_clone().map_err(Into::into);
} else if path == ".." {
@@ -682,19 +661,17 @@ impl Handle for VirtualDir {
Entry::Occupied(e) => {
let creat_excl_mask = types::Oflags::CREAT | types::Oflags::EXCL;
if (oflags & creat_excl_mask) == creat_excl_mask {
log::trace!("VirtualDir::openat was passed oflags CREAT|EXCL, but the file {:?} exists.", file_name);
log::trace!(" return Exist");
tracing::trace!("VirtualDir::openat was passed oflags CREAT|EXCL, but the file {:?} exists.", file_name);
return Err(Error::Exist);
}
if oflags.contains(&types::Oflags::DIRECTORY)
&& e.get().get_file_type() != types::Filetype::Directory
{
log::trace!(
tracing::trace!(
"VirtualDir::openat was passed oflags DIRECTORY, but {:?} is a file.",
file_name
);
log::trace!(" return Notdir");
return Err(Error::Notdir);
}
@@ -710,7 +687,7 @@ impl Handle for VirtualDir {
return Err(Error::Nospc);
}
log::trace!("VirtualDir::openat creating an InMemoryFile named {}", path);
tracing::trace!("VirtualDir::openat creating an InMemoryFile named {}", path);
let file = Box::new(InMemoryFile::memory_backed());
file.fd_flags.set(fd_flags);
@@ -758,7 +735,7 @@ impl Handle for VirtualDir {
Ok(())
}
Entry::Vacant(_) => {
log::trace!(
tracing::trace!(
"VirtualDir::remove_directory failed to remove {}, no such entry",
trimmed_path
);
@@ -799,7 +776,7 @@ impl Handle for VirtualDir {
Ok(())
}
Entry::Vacant(_) => {
log::trace!(
tracing::trace!(
"VirtualDir::unlink_file failed to remove {}, no such entry",
trimmed_path
);

View File

@@ -96,7 +96,7 @@ fn generate_wrappers(func: &witx::InterfaceFunc, old: bool) -> TokenStream {
let ret = #call
.err()
.unwrap_or(super::wasi::WasiError::ESUCCESS);
log::trace!(" | errno={}", ret);
tracing::trace!(" | errno={}", ret);
ret.as_raw_errno()
}
};

View File

@@ -201,7 +201,7 @@ pub fn define_struct(args: TokenStream) -> TokenStream {
let #name_ident = wasmtime::Func::wrap(
store,
move |caller: wasmtime::Caller<'_> #(,#shim_arg_decls)*| -> #ret_ty {
log::trace!(
tracing::trace!(
#format_str,
#(#format_args),*
);
@@ -209,7 +209,7 @@ pub fn define_struct(args: TokenStream) -> TokenStream {
let memory = match caller.get_export("memory") {
Some(wasmtime::Extern::Memory(m)) => m,
_ => {
log::warn!("callee does not export a memory as \"memory\"");
tracing::warn!("callee does not export a memory as \"memory\"");
let e = wasi_common::old::snapshot_0::wasi::__WASI_ERRNO_INVAL;
#handle_early_error
}

View File

@@ -9,7 +9,7 @@ repository = "https://github.com/bytecodealliance/wasmtime"
edition = "2018"
[dependencies]
log = "0.4"
tracing = "0.1.15"
libc = { version = "0.2", features = ["extra_traits"] }
bitflags = "1.2"
cfg-if = "0.1.9"

View File

@@ -25,7 +25,7 @@ impl FileTimeExt for filetime::FileTime {
let sec = match self.seconds().try_into() {
Ok(sec) => sec,
Err(_) => {
log::debug!("filetime_to_timespec failed converting seconds to required width");
tracing::debug!("filetime_to_timespec failed converting seconds to required width");
return Err(Error::from_raw_os_error(libc::EOVERFLOW));
}
};

View File

@@ -51,7 +51,9 @@ pub unsafe fn posix_fadvise(
// is providing a dubiously large hint. This is not confirmed (no helpful info in the man
// pages), but offhand, a 2+ GiB advisory read async seems unlikely to help with any kind
// of performance, so we log and exit early with a no-op.
log::warn!("`len` too big to fit in the host's command. Returning early with no-op!");
tracing::warn!(
"`len` too big to fit in the host's command. Returning early with no-op!"
);
return Ok(());
}
};

View File

@@ -13,7 +13,7 @@ edition = "2018"
[dependencies]
anyhow = "1.0"
log = { version = "0.4.8", default-features = false }
tracing = "0.1.15"
wasi-common = { path = "../wasi-common", version = "0.19.0" }
wasmtime = { path = "../wasmtime", version = "0.19.0", default-features = false }
wasmtime-runtime = { path = "../runtime", version = "0.19.0" }

View File

@@ -229,7 +229,8 @@
//! # }
//! ```
#![deny(missing_docs, intra_doc_link_resolution_failure)]
#![allow(unknown_lints)]
#![deny(missing_docs, broken_intra_doc_links)]
#![doc(test(attr(deny(warnings))))]
#![doc(test(attr(allow(dead_code, unused_variables, unused_mut))))]

View File

@@ -17,6 +17,3 @@ wast = "22.0.0"
[badges]
maintenance = { status = "actively-developed" }
[features]
lightbeam = ["wasmtime/lightbeam"]

View File

@@ -17,6 +17,7 @@ target-lexicon = "0.10"
peepmatic-fuzzing = { path = "../cranelift/peepmatic/crates/fuzzing", optional = true }
wasmtime = { path = "../crates/wasmtime" }
wasmtime-fuzzing = { path = "../crates/fuzzing" }
wasm-smith = "0.1.1"
[[bin]]
name = "compile"
@@ -100,3 +101,9 @@ required-features = ["peepmatic-fuzzing"]
[features]
binaryen = ["wasmtime-fuzzing/binaryen"]
[[bin]]
name = "instantiate-wasm-smith"
path = "fuzz_targets/instantiate-wasm-smith.rs"
test = false
doc = false

View File

@@ -0,0 +1,13 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use wasm_smith::Module;
use wasmtime::Strategy;
use wasmtime_fuzzing::oracles;
fuzz_target!(|module: Module| {
let mut module = module;
module.ensure_termination(1000);
let wasm_bytes = module.to_bytes();
oracles::instantiate(&wasm_bytes, Strategy::Auto);
});

View File

@@ -34,7 +34,6 @@ const CRATES_TO_PUBLISH: &[&str] = &[
"cranelift-preopt",
"cranelift-frontend",
"cranelift-wasm",
"cranelift-faerie",
"cranelift-native",
"cranelift-object",
"cranelift-interpreter",
@@ -57,6 +56,8 @@ const CRATES_TO_PUBLISH: &[&str] = &[
"wasmtime-debug",
"wasmtime-profiling",
"wasmtime-obj",
"wasmtime-cranelift",
"wasmtime-lightbeam",
"wasmtime-jit",
"wasmtime-cache",
"wasmtime",
@@ -299,7 +300,7 @@ fn verify(crates: &[Crate]) {
.arg("--manifest-path")
.arg(&krate.manifest)
.env("CARGO_TARGET_DIR", "./target");
if krate.name == "lightbeam" || krate.name == "witx" {
if krate.name.contains("lightbeam") || krate.name == "witx" {
cmd.arg("--no-verify");
}
let status = cmd.status().unwrap();

View File

@@ -2,8 +2,6 @@ use anyhow::{bail, Context as _, Result};
use object::write::Object;
use target_lexicon::Triple;
use wasmtime::Strategy;
#[cfg(feature = "lightbeam")]
use wasmtime_environ::Lightbeam;
use wasmtime_environ::{settings, settings::Configurable, ModuleEnvironment, Tunables};
use wasmtime_jit::{native, Compiler};