Add a stack_check instruction.
This instruction loads a stack limit from a global variable and compares it to the stack pointer, trapping if the stack has grown beyond the limit. Also add a expand_flags transform group containing legalization patterns for ISAs with CPU flags. Fixes #234.
This commit is contained in:
@@ -511,6 +511,11 @@ instructions before instruction selection::
|
||||
v9 = stack_addr ss3, 16
|
||||
v1 = load.f64 v9
|
||||
|
||||
When Cretonne code is running in a sandbox, it can also be necessary to include
|
||||
stack overflow checks in the prologue.
|
||||
|
||||
.. autoinst:: stack_check
|
||||
|
||||
Global variables
|
||||
----------------
|
||||
|
||||
|
||||
@@ -12,9 +12,8 @@ ebb0(v1: i32):
|
||||
trapz v1, user67
|
||||
return
|
||||
; check: $ebb0($v1: i32
|
||||
; nextln: brnz $v1, $(new=$EBB)
|
||||
; nextln: trap user67
|
||||
; check: $new:
|
||||
; nextln: $(f=$V) = ifcmp_imm $v1, 0
|
||||
; nextln: trapif eq $f, user67
|
||||
; nextln: return
|
||||
}
|
||||
|
||||
@@ -23,9 +22,8 @@ ebb0(v1: i32):
|
||||
trapnz v1, int_ovf
|
||||
return
|
||||
; check: $ebb0($v1: i32
|
||||
; nextln: brz $v1, $(new=$EBB)
|
||||
; nextln: trap int_ovf
|
||||
; check: $new:
|
||||
; nextln: $(f=$V) = ifcmp_imm $v1, 0
|
||||
; nextln: trapif ne $f, int_ovf
|
||||
; nextln: return
|
||||
}
|
||||
|
||||
|
||||
@@ -109,3 +109,17 @@ ebb0(v0: i32, v999: i64):
|
||||
; nextln: $v2 = load.f32 $v1+0x7fff_ffff
|
||||
return v2
|
||||
}
|
||||
|
||||
; Stack overflow check.
|
||||
; The stack limit is stored in a pointer-sized global variable.
|
||||
function %stkchk(i64 vmctx) spiderwasm {
|
||||
gv0 = vmctx+64
|
||||
|
||||
ebb0(v0: i64):
|
||||
; check: $ebb0(
|
||||
stack_check gv0
|
||||
; check: $(limit=$V) = load.i64 notrap aligned
|
||||
; check: $(flags=$V) = ifcmp_sp $limit
|
||||
; check: trapif uge $flags, stk_ovf
|
||||
return
|
||||
}
|
||||
|
||||
@@ -567,6 +567,21 @@ copy_special = Instruction(
|
||||
ins=(src, dst),
|
||||
other_side_effects=True)
|
||||
|
||||
GV = Operand(
|
||||
'GV', entities.global_var, doc=r"""
|
||||
Global variable containing the stack limit.
|
||||
""")
|
||||
|
||||
stack_check = Instruction(
|
||||
'stack_check', r"""
|
||||
Check for stack overflow.
|
||||
|
||||
Read the stack limit from ``GV`` and compare it to the stack pointer. If
|
||||
the stack pointer has reached or exceeded the limit, generate a trap with a
|
||||
``stk_ovf`` code.
|
||||
""",
|
||||
ins=GV, can_trap=True)
|
||||
|
||||
StackOffset = Operand('Offset', imm64, 'Offset from current stack pointer')
|
||||
adjust_sp_imm = Instruction(
|
||||
'adjust_sp_imm', r"""
|
||||
|
||||
@@ -7,7 +7,7 @@ patterns that describe how base instructions can be transformed to other base
|
||||
instructions that are legal.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from .immediates import intcc, ieee32, ieee64
|
||||
from .immediates import intcc, imm64, ieee32, ieee64
|
||||
from . import instructions as insts
|
||||
from . import types
|
||||
from .instructions import iadd, iadd_cout, iadd_cin, iadd_carry, iadd_imm
|
||||
@@ -47,6 +47,15 @@ expand = XFormGroup('expand', """
|
||||
operating on the same types as the original instructions.
|
||||
""")
|
||||
|
||||
expand_flags = XFormGroup('expand_flags', """
|
||||
Instruction expansions for architectures with flags.
|
||||
|
||||
Expand some instructions using CPU flags, then fall back to the normal
|
||||
expansions. Not all architectures support CPU flags, so these patterns
|
||||
are kept separate.
|
||||
""", chain=expand)
|
||||
|
||||
|
||||
# Custom expansions for memory objects.
|
||||
expand.custom_legalize(insts.global_addr, 'expand_global_addr')
|
||||
expand.custom_legalize(insts.heap_addr, 'expand_heap_addr')
|
||||
@@ -245,3 +254,20 @@ for ty, minus_zero in [
|
||||
a2 << band(y, b),
|
||||
a << bor(a1, a2)
|
||||
))
|
||||
|
||||
|
||||
# Expansions using CPU flags.
|
||||
expand_flags.custom_legalize(insts.stack_check, 'expand_stack_check')
|
||||
|
||||
expand_flags.legalize(
|
||||
insts.trapnz(x, c),
|
||||
Rtl(
|
||||
a << insts.ifcmp_imm(x, imm64(0)),
|
||||
insts.trapif(intcc.ne, a, c)
|
||||
))
|
||||
expand_flags.legalize(
|
||||
insts.trapz(x, c),
|
||||
Rtl(
|
||||
a << insts.ifcmp_imm(x, imm64(0)),
|
||||
insts.trapif(intcc.eq, a, c)
|
||||
))
|
||||
|
||||
@@ -10,7 +10,7 @@ from . import recipes as r
|
||||
from . import settings as cfg
|
||||
from . import instructions as x86
|
||||
from .legalize import intel_expand
|
||||
from base.legalize import narrow, expand
|
||||
from base.legalize import narrow, expand_flags
|
||||
from base.settings import allones_funcaddrs, is_pic
|
||||
from .settings import use_sse41
|
||||
|
||||
@@ -22,18 +22,18 @@ except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
I32.legalize_monomorphic(expand)
|
||||
I32.legalize_monomorphic(expand_flags)
|
||||
I32.legalize_type(
|
||||
default=narrow,
|
||||
b1=expand,
|
||||
b1=expand_flags,
|
||||
i32=intel_expand,
|
||||
f32=intel_expand,
|
||||
f64=intel_expand)
|
||||
|
||||
I64.legalize_monomorphic(expand)
|
||||
I64.legalize_monomorphic(expand_flags)
|
||||
I64.legalize_type(
|
||||
default=narrow,
|
||||
b1=expand,
|
||||
b1=expand_flags,
|
||||
i32=intel_expand,
|
||||
i64=intel_expand,
|
||||
f32=intel_expand,
|
||||
|
||||
@@ -18,7 +18,7 @@ intel_expand = XFormGroup(
|
||||
|
||||
Use Intel-specific instructions if needed.
|
||||
""",
|
||||
isa=ISA, chain=shared.expand)
|
||||
isa=ISA, chain=shared.expand_flags)
|
||||
|
||||
a = Var('a')
|
||||
dead = Var('dead')
|
||||
|
||||
@@ -267,3 +267,39 @@ fn expand_fconst(
|
||||
};
|
||||
pos.func.dfg.replace(inst).bitcast(ty, ival);
|
||||
}
|
||||
|
||||
/// Expand the stack check instruction.
|
||||
pub fn expand_stack_check(
|
||||
inst: ir::Inst,
|
||||
func: &mut ir::Function,
|
||||
_cfg: &mut ControlFlowGraph,
|
||||
isa: &TargetIsa,
|
||||
) {
|
||||
use ir::condcodes::IntCC;
|
||||
|
||||
let gv = match func.dfg[inst] {
|
||||
ir::InstructionData::UnaryGlobalVar { global_var, .. } => global_var,
|
||||
_ => panic!("Want stack_check: {}", func.dfg.display_inst(inst, isa)),
|
||||
};
|
||||
let ptr_ty = if isa.flags().is_64bit() {
|
||||
ir::types::I64
|
||||
} else {
|
||||
ir::types::I32
|
||||
};
|
||||
|
||||
let mut pos = FuncCursor::new(func).at_inst(inst);
|
||||
pos.use_srcloc(inst);
|
||||
|
||||
let limit_addr = pos.ins().global_addr(ptr_ty, gv);
|
||||
|
||||
let mut mflags = ir::MemFlags::new();
|
||||
mflags.set_aligned();
|
||||
mflags.set_notrap();
|
||||
let limit = pos.ins().load(ptr_ty, mflags, limit_addr, 0);
|
||||
let cflags = pos.ins().ifcmp_sp(limit);
|
||||
pos.func.dfg.replace(inst).trapif(
|
||||
IntCC::UnsignedGreaterThanOrEqual,
|
||||
cflags,
|
||||
ir::TrapCode::StackOverflow,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user