Two Lucet-related fixes to stack overflow handling.
Lucet uses stack probes rather than explicit stack limit checks as Wasmtime does. In bytecodealliance/lucet#616, I have discovered that I previously was not running some Lucet runtime tests with the new backend, so was missing some test failures due to missing pieces in the new backend. This PR adds (i) calls to probestack, when enabled, in the prologue of every function with a stack frame larger than one page (configurable via flags); and (ii) trap metadata for every instruction on x86-64 that can access the stack, hence be the first point at which a stack overflow is detected when the stack pointer is decremented.
This commit is contained in:
13
.github/workflows/main.yml
vendored
13
.github/workflows/main.yml
vendored
@@ -60,7 +60,10 @@ jobs:
|
|||||||
# nightly-only feature right now.
|
# nightly-only feature right now.
|
||||||
- uses: ./.github/actions/install-rust
|
- uses: ./.github/actions/install-rust
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
# TODO (rust-lang/rust#79661): We are seeing an internal compiler error when
|
||||||
|
# building with the latest (2020-12-06) nightly; pin on a slightly older
|
||||||
|
# version for now.
|
||||||
|
toolchain: nightly-2020-11-29
|
||||||
- run: cargo doc --no-deps --all --exclude wasmtime-cli --exclude test-programs --exclude cranelift-codegen-meta
|
- run: cargo doc --no-deps --all --exclude wasmtime-cli --exclude test-programs --exclude cranelift-codegen-meta
|
||||||
- run: cargo doc --package cranelift-codegen-meta --document-private-items
|
- run: cargo doc --package cranelift-codegen-meta --document-private-items
|
||||||
- uses: actions/upload-artifact@v1
|
- uses: actions/upload-artifact@v1
|
||||||
@@ -145,7 +148,7 @@ jobs:
|
|||||||
# flags to rustc.
|
# flags to rustc.
|
||||||
- uses: ./.github/actions/install-rust
|
- uses: ./.github/actions/install-rust
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: nightly-2020-11-29
|
||||||
- run: cargo install cargo-fuzz --vers "^0.8"
|
- run: cargo install cargo-fuzz --vers "^0.8"
|
||||||
- run: cargo fetch
|
- run: cargo fetch
|
||||||
working-directory: ./fuzz
|
working-directory: ./fuzz
|
||||||
@@ -202,7 +205,7 @@ jobs:
|
|||||||
rust: beta
|
rust: beta
|
||||||
- build: nightly
|
- build: nightly
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
rust: nightly
|
rust: nightly-2020-11-29
|
||||||
- build: macos
|
- build: macos
|
||||||
os: macos-latest
|
os: macos-latest
|
||||||
rust: stable
|
rust: stable
|
||||||
@@ -292,7 +295,7 @@ jobs:
|
|||||||
submodules: true
|
submodules: true
|
||||||
- uses: ./.github/actions/install-rust
|
- uses: ./.github/actions/install-rust
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: nightly-2020-11-29
|
||||||
- uses: ./.github/actions/define-llvm-env
|
- uses: ./.github/actions/define-llvm-env
|
||||||
|
|
||||||
# Install wasm32 targets in order to build various tests throughout the
|
# Install wasm32 targets in order to build various tests throughout the
|
||||||
@@ -303,7 +306,7 @@ jobs:
|
|||||||
# Run the x64 CI script.
|
# Run the x64 CI script.
|
||||||
- run: ./ci/run-experimental-x64-ci.sh
|
- run: ./ci/run-experimental-x64-ci.sh
|
||||||
env:
|
env:
|
||||||
CARGO_VERSION: "+nightly"
|
CARGO_VERSION: "+nightly-2020-11-29"
|
||||||
RUST_BACKTRACE: 1
|
RUST_BACKTRACE: 1
|
||||||
|
|
||||||
# Build and test the wasi-nn module.
|
# Build and test the wasi-nn module.
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use crate::{CodegenError, CodegenResult};
|
|||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use regalloc::{RealReg, Reg, RegClass, Set, Writable};
|
use regalloc::{RealReg, Reg, RegClass, Set, Writable};
|
||||||
use smallvec::SmallVec;
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
// We use a generic implementation that factors out AArch64 and x64 ABI commonalities, because
|
// We use a generic implementation that factors out AArch64 and x64 ABI commonalities, because
|
||||||
// these ABIs are very similar.
|
// these ABIs are very similar.
|
||||||
@@ -508,6 +508,12 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||||||
insts
|
insts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn gen_probestack(_: u32) -> SmallVec<[Self::I; 2]> {
|
||||||
|
// TODO: implement if we ever require stack probes on an AArch64 host
|
||||||
|
// (unlikely unless Lucet is ported)
|
||||||
|
smallvec![]
|
||||||
|
}
|
||||||
|
|
||||||
// Returns stack bytes used as well as instructions. Does not adjust
|
// Returns stack bytes used as well as instructions. Does not adjust
|
||||||
// nominal SP offset; abi_impl generic code will do that.
|
// nominal SP offset; abi_impl generic code will do that.
|
||||||
fn gen_clobber_save(
|
fn gen_clobber_save(
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use crate::{CodegenError, CodegenResult};
|
|||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use regalloc::{RealReg, Reg, RegClass, Set, Writable};
|
use regalloc::{RealReg, Reg, RegClass, Set, Writable};
|
||||||
use smallvec::SmallVec;
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
/// Support for the ARM ABI from the callee side (within a function body).
|
/// Support for the ARM ABI from the callee side (within a function body).
|
||||||
pub(crate) type Arm32ABICallee = ABICalleeImpl<Arm32MachineDeps>;
|
pub(crate) type Arm32ABICallee = ABICalleeImpl<Arm32MachineDeps>;
|
||||||
@@ -305,6 +305,12 @@ impl ABIMachineSpec for Arm32MachineDeps {
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn gen_probestack(_: u32) -> SmallVec<[Self::I; 2]> {
|
||||||
|
// TODO: implement if we ever require stack probes on ARM32 (unlikely
|
||||||
|
// unless Lucet is ported)
|
||||||
|
smallvec![]
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns stack bytes used as well as instructions. Does not adjust
|
/// Returns stack bytes used as well as instructions. Does not adjust
|
||||||
/// nominal SP offset; caller will do that.
|
/// nominal SP offset; caller will do that.
|
||||||
fn gen_clobber_save(
|
fn gen_clobber_save(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Implementation of the standard x64 ABI.
|
//! Implementation of the standard x64 ABI.
|
||||||
|
|
||||||
use crate::ir::types::*;
|
use crate::ir::types::*;
|
||||||
use crate::ir::{self, types, MemFlags, TrapCode, Type};
|
use crate::ir::{self, types, ExternalName, LibCall, MemFlags, Opcode, TrapCode, Type};
|
||||||
use crate::isa;
|
use crate::isa;
|
||||||
use crate::isa::{x64::inst::*, CallConv};
|
use crate::isa::{x64::inst::*, CallConv};
|
||||||
use crate::machinst::abi_impl::*;
|
use crate::machinst::abi_impl::*;
|
||||||
@@ -389,6 +389,22 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
|||||||
insts
|
insts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn gen_probestack(frame_size: u32) -> SmallVec<[Self::I; 2]> {
|
||||||
|
let mut insts = SmallVec::new();
|
||||||
|
insts.push(Inst::imm(
|
||||||
|
OperandSize::Size32,
|
||||||
|
frame_size as u64,
|
||||||
|
Writable::from_reg(regs::rax()),
|
||||||
|
));
|
||||||
|
insts.push(Inst::CallKnown {
|
||||||
|
dest: ExternalName::LibCall(LibCall::Probestack),
|
||||||
|
uses: vec![regs::rax()],
|
||||||
|
defs: vec![],
|
||||||
|
opcode: Opcode::Call,
|
||||||
|
});
|
||||||
|
insts
|
||||||
|
}
|
||||||
|
|
||||||
fn gen_clobber_save(
|
fn gen_clobber_save(
|
||||||
call_conv: isa::CallConv,
|
call_conv: isa::CallConv,
|
||||||
_: &settings::Flags,
|
_: &settings::Flags,
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ impl LegacyPrefixes {
|
|||||||
fn emit_std_enc_mem(
|
fn emit_std_enc_mem(
|
||||||
sink: &mut MachBuffer<Inst>,
|
sink: &mut MachBuffer<Inst>,
|
||||||
state: &EmitState,
|
state: &EmitState,
|
||||||
|
info: &EmitInfo,
|
||||||
prefixes: LegacyPrefixes,
|
prefixes: LegacyPrefixes,
|
||||||
opcodes: u32,
|
opcodes: u32,
|
||||||
mut num_opcodes: usize,
|
mut num_opcodes: usize,
|
||||||
@@ -194,7 +195,8 @@ fn emit_std_enc_mem(
|
|||||||
// expression. But `enc_g` can be derived from a register of any class.
|
// expression. But `enc_g` can be derived from a register of any class.
|
||||||
|
|
||||||
let srcloc = state.cur_srcloc();
|
let srcloc = state.cur_srcloc();
|
||||||
if srcloc != SourceLoc::default() && mem_e.can_trap() {
|
let can_trap = mem_e.can_trap();
|
||||||
|
if srcloc != SourceLoc::default() && can_trap {
|
||||||
sink.add_trap(srcloc, TrapCode::HeapOutOfBounds);
|
sink.add_trap(srcloc, TrapCode::HeapOutOfBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,6 +204,12 @@ fn emit_std_enc_mem(
|
|||||||
|
|
||||||
match mem_e {
|
match mem_e {
|
||||||
Amode::ImmReg { simm32, base, .. } => {
|
Amode::ImmReg { simm32, base, .. } => {
|
||||||
|
// If this is an access based off of RSP, it may trap with a stack overflow if it's the
|
||||||
|
// first touch of a new stack page.
|
||||||
|
if *base == regs::rsp() && !can_trap && info.flags().enable_probestack() {
|
||||||
|
sink.add_trap(srcloc, TrapCode::StackOverflow);
|
||||||
|
}
|
||||||
|
|
||||||
// First, the REX byte.
|
// First, the REX byte.
|
||||||
let enc_e = int_reg_enc(*base);
|
let enc_e = int_reg_enc(*base);
|
||||||
rex.emit_two_op(sink, enc_g, enc_e);
|
rex.emit_two_op(sink, enc_g, enc_e);
|
||||||
@@ -262,6 +270,12 @@ fn emit_std_enc_mem(
|
|||||||
shift,
|
shift,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
|
// If this is an access based off of RSP, it may trap with a stack overflow if it's the
|
||||||
|
// first touch of a new stack page.
|
||||||
|
if *reg_base == regs::rsp() && !can_trap && info.flags().enable_probestack() {
|
||||||
|
sink.add_trap(srcloc, TrapCode::StackOverflow);
|
||||||
|
}
|
||||||
|
|
||||||
let enc_base = int_reg_enc(*reg_base);
|
let enc_base = int_reg_enc(*reg_base);
|
||||||
let enc_index = int_reg_enc(*reg_index);
|
let enc_index = int_reg_enc(*reg_index);
|
||||||
|
|
||||||
@@ -350,6 +364,7 @@ fn emit_std_enc_enc(
|
|||||||
fn emit_std_reg_mem(
|
fn emit_std_reg_mem(
|
||||||
sink: &mut MachBuffer<Inst>,
|
sink: &mut MachBuffer<Inst>,
|
||||||
state: &EmitState,
|
state: &EmitState,
|
||||||
|
info: &EmitInfo,
|
||||||
prefixes: LegacyPrefixes,
|
prefixes: LegacyPrefixes,
|
||||||
opcodes: u32,
|
opcodes: u32,
|
||||||
num_opcodes: usize,
|
num_opcodes: usize,
|
||||||
@@ -361,6 +376,7 @@ fn emit_std_reg_mem(
|
|||||||
emit_std_enc_mem(
|
emit_std_enc_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
prefixes,
|
prefixes,
|
||||||
opcodes,
|
opcodes,
|
||||||
num_opcodes,
|
num_opcodes,
|
||||||
@@ -538,6 +554,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_reg_mem(
|
emit_std_reg_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
LegacyPrefixes::None,
|
LegacyPrefixes::None,
|
||||||
0x0FAF,
|
0x0FAF,
|
||||||
2,
|
2,
|
||||||
@@ -597,6 +614,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_reg_mem(
|
emit_std_reg_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
LegacyPrefixes::None,
|
LegacyPrefixes::None,
|
||||||
opcode_m,
|
opcode_m,
|
||||||
1,
|
1,
|
||||||
@@ -654,6 +672,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_reg_mem(
|
emit_std_reg_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
prefix,
|
prefix,
|
||||||
opcode,
|
opcode,
|
||||||
num_opcodes,
|
num_opcodes,
|
||||||
@@ -717,7 +736,9 @@ pub(crate) fn emit(
|
|||||||
}
|
}
|
||||||
RegMem::Mem { addr: src } => {
|
RegMem::Mem { addr: src } => {
|
||||||
let amode = src.finalize(state, sink);
|
let amode = src.finalize(state, sink);
|
||||||
emit_std_enc_mem(sink, state, prefix, opcode, 1, subopcode, &amode, rex_flags);
|
emit_std_enc_mem(
|
||||||
|
sink, state, info, prefix, opcode, 1, subopcode, &amode, rex_flags,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -738,7 +759,9 @@ pub(crate) fn emit(
|
|||||||
}
|
}
|
||||||
RegMem::Mem { addr: src } => {
|
RegMem::Mem { addr: src } => {
|
||||||
let amode = src.finalize(state, sink);
|
let amode = src.finalize(state, sink);
|
||||||
emit_std_enc_mem(sink, state, prefix, 0xF7, 1, subopcode, &amode, rex_flags);
|
emit_std_enc_mem(
|
||||||
|
sink, state, info, prefix, 0xF7, 1, subopcode, &amode, rex_flags,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -987,6 +1010,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_reg_mem(
|
emit_std_reg_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
LegacyPrefixes::None,
|
LegacyPrefixes::None,
|
||||||
opcodes,
|
opcodes,
|
||||||
num_opcodes,
|
num_opcodes,
|
||||||
@@ -1004,6 +1028,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_reg_mem(
|
emit_std_reg_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
LegacyPrefixes::None,
|
LegacyPrefixes::None,
|
||||||
0x8B,
|
0x8B,
|
||||||
1,
|
1,
|
||||||
@@ -1019,6 +1044,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_reg_mem(
|
emit_std_reg_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
LegacyPrefixes::None,
|
LegacyPrefixes::None,
|
||||||
0x8D,
|
0x8D,
|
||||||
1,
|
1,
|
||||||
@@ -1081,6 +1107,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_reg_mem(
|
emit_std_reg_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
LegacyPrefixes::None,
|
LegacyPrefixes::None,
|
||||||
opcodes,
|
opcodes,
|
||||||
num_opcodes,
|
num_opcodes,
|
||||||
@@ -1108,7 +1135,17 @@ pub(crate) fn emit(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// MOV r8, r/m8 is (REX.W==0) 88 /r
|
// MOV r8, r/m8 is (REX.W==0) 88 /r
|
||||||
emit_std_reg_mem(sink, state, LegacyPrefixes::None, 0x88, 1, *src, dst, rex)
|
emit_std_reg_mem(
|
||||||
|
sink,
|
||||||
|
state,
|
||||||
|
info,
|
||||||
|
LegacyPrefixes::None,
|
||||||
|
0x88,
|
||||||
|
1,
|
||||||
|
*src,
|
||||||
|
dst,
|
||||||
|
rex,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
2 => {
|
2 => {
|
||||||
@@ -1116,6 +1153,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_reg_mem(
|
emit_std_reg_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
LegacyPrefixes::_66,
|
LegacyPrefixes::_66,
|
||||||
0x89,
|
0x89,
|
||||||
1,
|
1,
|
||||||
@@ -1130,6 +1168,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_reg_mem(
|
emit_std_reg_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
LegacyPrefixes::None,
|
LegacyPrefixes::None,
|
||||||
0x89,
|
0x89,
|
||||||
1,
|
1,
|
||||||
@@ -1144,6 +1183,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_reg_mem(
|
emit_std_reg_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
LegacyPrefixes::None,
|
LegacyPrefixes::None,
|
||||||
0x89,
|
0x89,
|
||||||
1,
|
1,
|
||||||
@@ -1253,6 +1293,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_reg_mem(
|
emit_std_reg_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
prefix,
|
prefix,
|
||||||
opcode_bytes,
|
opcode_bytes,
|
||||||
2,
|
2,
|
||||||
@@ -1311,7 +1352,7 @@ pub(crate) fn emit(
|
|||||||
let addr = &addr.finalize(state, sink);
|
let addr = &addr.finalize(state, sink);
|
||||||
// Whereas here we revert to the "normal" G-E ordering.
|
// Whereas here we revert to the "normal" G-E ordering.
|
||||||
let opcode = if *size == 1 { 0x3A } else { 0x3B };
|
let opcode = if *size == 1 { 0x3A } else { 0x3B };
|
||||||
emit_std_reg_mem(sink, state, prefix, opcode, 1, *reg_g, addr, rex);
|
emit_std_reg_mem(sink, state, info, prefix, opcode, 1, *reg_g, addr, rex);
|
||||||
}
|
}
|
||||||
|
|
||||||
RegMemImm::Imm { simm32 } => {
|
RegMemImm::Imm { simm32 } => {
|
||||||
@@ -1372,6 +1413,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_reg_mem(
|
emit_std_reg_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
prefix,
|
prefix,
|
||||||
opcode,
|
opcode,
|
||||||
2,
|
2,
|
||||||
@@ -1408,6 +1450,10 @@ pub(crate) fn emit(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Inst::Push64 { src } => {
|
Inst::Push64 { src } => {
|
||||||
|
if info.flags().enable_probestack() {
|
||||||
|
sink.add_trap(state.cur_srcloc(), TrapCode::StackOverflow);
|
||||||
|
}
|
||||||
|
|
||||||
match src {
|
match src {
|
||||||
RegMemImm::Reg { reg } => {
|
RegMemImm::Reg { reg } => {
|
||||||
let enc_reg = int_reg_enc(*reg);
|
let enc_reg = int_reg_enc(*reg);
|
||||||
@@ -1423,6 +1469,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_enc_mem(
|
emit_std_enc_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
LegacyPrefixes::None,
|
LegacyPrefixes::None,
|
||||||
0xFF,
|
0xFF,
|
||||||
1,
|
1,
|
||||||
@@ -1454,6 +1501,9 @@ pub(crate) fn emit(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Inst::CallKnown { dest, opcode, .. } => {
|
Inst::CallKnown { dest, opcode, .. } => {
|
||||||
|
if info.flags().enable_probestack() {
|
||||||
|
sink.add_trap(state.cur_srcloc(), TrapCode::StackOverflow);
|
||||||
|
}
|
||||||
if let Some(s) = state.take_stack_map() {
|
if let Some(s) = state.take_stack_map() {
|
||||||
sink.add_stack_map(StackMapExtent::UpcomingBytes(5), s);
|
sink.add_stack_map(StackMapExtent::UpcomingBytes(5), s);
|
||||||
}
|
}
|
||||||
@@ -1469,6 +1519,9 @@ pub(crate) fn emit(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Inst::CallUnknown { dest, opcode, .. } => {
|
Inst::CallUnknown { dest, opcode, .. } => {
|
||||||
|
if info.flags().enable_probestack() {
|
||||||
|
sink.add_trap(state.cur_srcloc(), TrapCode::StackOverflow);
|
||||||
|
}
|
||||||
let start_offset = sink.cur_offset();
|
let start_offset = sink.cur_offset();
|
||||||
match dest {
|
match dest {
|
||||||
RegMem::Reg { reg } => {
|
RegMem::Reg { reg } => {
|
||||||
@@ -1489,6 +1542,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_enc_mem(
|
emit_std_enc_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
LegacyPrefixes::None,
|
LegacyPrefixes::None,
|
||||||
0xFF,
|
0xFF,
|
||||||
1,
|
1,
|
||||||
@@ -1587,6 +1641,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_enc_mem(
|
emit_std_enc_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
LegacyPrefixes::None,
|
LegacyPrefixes::None,
|
||||||
0xFF,
|
0xFF,
|
||||||
1,
|
1,
|
||||||
@@ -1733,6 +1788,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_reg_mem(
|
emit_std_reg_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
prefix,
|
prefix,
|
||||||
opcode,
|
opcode,
|
||||||
num_opcodes,
|
num_opcodes,
|
||||||
@@ -1848,6 +1904,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_reg_mem(
|
emit_std_reg_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
prefix,
|
prefix,
|
||||||
opcode,
|
opcode,
|
||||||
length,
|
length,
|
||||||
@@ -1994,7 +2051,17 @@ pub(crate) fn emit(
|
|||||||
!regs_swapped,
|
!regs_swapped,
|
||||||
"No existing way to encode a mem argument in the ModRM r/m field."
|
"No existing way to encode a mem argument in the ModRM r/m field."
|
||||||
);
|
);
|
||||||
emit_std_reg_mem(sink, state, prefix, opcode, len, dst.to_reg(), addr, rex);
|
emit_std_reg_mem(
|
||||||
|
sink,
|
||||||
|
state,
|
||||||
|
info,
|
||||||
|
prefix,
|
||||||
|
opcode,
|
||||||
|
len,
|
||||||
|
dst.to_reg(),
|
||||||
|
addr,
|
||||||
|
rex,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sink.put1(*imm);
|
sink.put1(*imm);
|
||||||
@@ -2027,6 +2094,7 @@ pub(crate) fn emit(
|
|||||||
emit_std_reg_mem(
|
emit_std_reg_mem(
|
||||||
sink,
|
sink,
|
||||||
state,
|
state,
|
||||||
|
info,
|
||||||
prefix,
|
prefix,
|
||||||
opcode,
|
opcode,
|
||||||
2,
|
2,
|
||||||
@@ -2091,7 +2159,17 @@ pub(crate) fn emit(
|
|||||||
}
|
}
|
||||||
RegMem::Mem { addr } => {
|
RegMem::Mem { addr } => {
|
||||||
let addr = &addr.finalize(state, sink);
|
let addr = &addr.finalize(state, sink);
|
||||||
emit_std_reg_mem(sink, state, prefix, opcode, 2, reg_g.to_reg(), addr, rex);
|
emit_std_reg_mem(
|
||||||
|
sink,
|
||||||
|
state,
|
||||||
|
info,
|
||||||
|
prefix,
|
||||||
|
opcode,
|
||||||
|
2,
|
||||||
|
reg_g.to_reg(),
|
||||||
|
addr,
|
||||||
|
rex,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2111,7 +2189,7 @@ pub(crate) fn emit(
|
|||||||
}
|
}
|
||||||
RegMem::Mem { addr } => {
|
RegMem::Mem { addr } => {
|
||||||
let addr = &addr.finalize(state, sink);
|
let addr = &addr.finalize(state, sink);
|
||||||
emit_std_reg_mem(sink, state, prefix, opcode, len, *dst, addr, rex);
|
emit_std_reg_mem(sink, state, info, prefix, opcode, len, *dst, addr, rex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2625,7 +2703,7 @@ pub(crate) fn emit(
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let amode = dst.finalize(state, sink);
|
let amode = dst.finalize(state, sink);
|
||||||
emit_std_reg_mem(sink, state, prefix, opcodes, 2, *src, &amode, rex);
|
emit_std_reg_mem(sink, state, info, prefix, opcodes, 2, *src, &amode, rex);
|
||||||
}
|
}
|
||||||
|
|
||||||
Inst::AtomicRmwSeq { ty, op } => {
|
Inst::AtomicRmwSeq { ty, op } => {
|
||||||
|
|||||||
@@ -4079,8 +4079,8 @@ impl LowerBackend for X64Backend {
|
|||||||
let ty = ctx.input_ty(ifcmp_sp, 0);
|
let ty = ctx.input_ty(ifcmp_sp, 0);
|
||||||
ctx.emit(Inst::cmp_rmi_r(
|
ctx.emit(Inst::cmp_rmi_r(
|
||||||
ty.bytes() as u8,
|
ty.bytes() as u8,
|
||||||
RegMemImm::reg(operand),
|
RegMemImm::reg(regs::rsp()),
|
||||||
regs::rsp(),
|
operand,
|
||||||
));
|
));
|
||||||
let cond_code = ctx.data(branches[0]).cond_code().unwrap();
|
let cond_code = ctx.data(branches[0]).cond_code().unwrap();
|
||||||
let cc = CC::from_intcc(cond_code);
|
let cc = CC::from_intcc(cond_code);
|
||||||
|
|||||||
@@ -314,6 +314,9 @@ pub trait ABIMachineSpec {
|
|||||||
/// Generate the usual frame-restore sequence for this architecture.
|
/// Generate the usual frame-restore sequence for this architecture.
|
||||||
fn gen_epilogue_frame_restore() -> SmallVec<[Self::I; 2]>;
|
fn gen_epilogue_frame_restore() -> SmallVec<[Self::I; 2]>;
|
||||||
|
|
||||||
|
/// Generate a probestack call.
|
||||||
|
fn gen_probestack(_frame_size: u32) -> SmallVec<[Self::I; 2]>;
|
||||||
|
|
||||||
/// Generate a clobber-save sequence. This takes the list of *all* registers
|
/// Generate a clobber-save sequence. This takes the list of *all* registers
|
||||||
/// written/modified by the function body. The implementation here is
|
/// written/modified by the function body. The implementation here is
|
||||||
/// responsible for determining which of these are callee-saved according to
|
/// responsible for determining which of these are callee-saved according to
|
||||||
@@ -481,6 +484,9 @@ pub struct ABICalleeImpl<M: ABIMachineSpec> {
|
|||||||
/// manually register-allocated and carefully only use caller-saved
|
/// manually register-allocated and carefully only use caller-saved
|
||||||
/// registers and keep nothing live after this sequence of instructions.
|
/// registers and keep nothing live after this sequence of instructions.
|
||||||
stack_limit: Option<(Reg, Vec<M::I>)>,
|
stack_limit: Option<(Reg, Vec<M::I>)>,
|
||||||
|
/// Are we to invoke the probestack function in the prologue? If so,
|
||||||
|
/// what is the minimum size at which we must invoke it?
|
||||||
|
probestack_min_frame: Option<u32>,
|
||||||
|
|
||||||
_mach: PhantomData<M>,
|
_mach: PhantomData<M>,
|
||||||
}
|
}
|
||||||
@@ -536,6 +542,18 @@ impl<M: ABIMachineSpec> ABICalleeImpl<M> {
|
|||||||
.map(|reg| (reg, Vec::new()))
|
.map(|reg| (reg, Vec::new()))
|
||||||
.or_else(|| f.stack_limit.map(|gv| gen_stack_limit::<M>(f, &sig, gv)));
|
.or_else(|| f.stack_limit.map(|gv| gen_stack_limit::<M>(f, &sig, gv)));
|
||||||
|
|
||||||
|
// Determine whether a probestack call is required for large enough
|
||||||
|
// frames (and the minimum frame size if so).
|
||||||
|
let probestack_min_frame = if flags.enable_probestack() {
|
||||||
|
assert!(
|
||||||
|
!flags.probestack_func_adjusts_sp(),
|
||||||
|
"SP-adjusting probestack not supported in new backends"
|
||||||
|
);
|
||||||
|
Some(1 << flags.probestack_size_log2())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
sig,
|
sig,
|
||||||
stackslots,
|
stackslots,
|
||||||
@@ -550,6 +568,7 @@ impl<M: ABIMachineSpec> ABICalleeImpl<M> {
|
|||||||
flags,
|
flags,
|
||||||
is_leaf: f.is_leaf(),
|
is_leaf: f.is_leaf(),
|
||||||
stack_limit,
|
stack_limit,
|
||||||
|
probestack_min_frame,
|
||||||
_mach: PhantomData,
|
_mach: PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -978,6 +997,11 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
|||||||
insts.extend_from_slice(stack_limit_load);
|
insts.extend_from_slice(stack_limit_load);
|
||||||
self.insert_stack_check(*reg, total_stacksize, &mut insts);
|
self.insert_stack_check(*reg, total_stacksize, &mut insts);
|
||||||
}
|
}
|
||||||
|
if let Some(min_frame) = &self.probestack_min_frame {
|
||||||
|
if total_stacksize >= *min_frame {
|
||||||
|
insts.extend(M::gen_probestack(total_stacksize));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if total_stacksize > 0 {
|
if total_stacksize > 0 {
|
||||||
self.fixed_frame_storage_size += total_stacksize;
|
self.fixed_frame_storage_size += total_stacksize;
|
||||||
|
|||||||
17
cranelift/filetests/filetests/isa/x64/probestack.clif
Normal file
17
cranelift/filetests/filetests/isa/x64/probestack.clif
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
test compile
|
||||||
|
set enable_probestack=true
|
||||||
|
target x86_64
|
||||||
|
feature "experimental_x64"
|
||||||
|
|
||||||
|
function %f1() -> i64 {
|
||||||
|
ss0 = explicit_slot 100000
|
||||||
|
|
||||||
|
block0:
|
||||||
|
v1 = stack_addr.i64 ss0
|
||||||
|
return v1
|
||||||
|
}
|
||||||
|
|
||||||
|
; check: pushq %rbp
|
||||||
|
; nextln: movq %rsp, %rbp
|
||||||
|
; nextln: movl $$100000, %eax
|
||||||
|
; nextln: call LibCall(Probestack)
|
||||||
Reference in New Issue
Block a user