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
|
v9 = stack_addr ss3, 16
|
||||||
v1 = load.f64 v9
|
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
|
Global variables
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ ebb0(v1: i32):
|
|||||||
trapz v1, user67
|
trapz v1, user67
|
||||||
return
|
return
|
||||||
; check: $ebb0($v1: i32
|
; check: $ebb0($v1: i32
|
||||||
; nextln: brnz $v1, $(new=$EBB)
|
; nextln: $(f=$V) = ifcmp_imm $v1, 0
|
||||||
; nextln: trap user67
|
; nextln: trapif eq $f, user67
|
||||||
; check: $new:
|
|
||||||
; nextln: return
|
; nextln: return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,9 +22,8 @@ ebb0(v1: i32):
|
|||||||
trapnz v1, int_ovf
|
trapnz v1, int_ovf
|
||||||
return
|
return
|
||||||
; check: $ebb0($v1: i32
|
; check: $ebb0($v1: i32
|
||||||
; nextln: brz $v1, $(new=$EBB)
|
; nextln: $(f=$V) = ifcmp_imm $v1, 0
|
||||||
; nextln: trap int_ovf
|
; nextln: trapif ne $f, int_ovf
|
||||||
; check: $new:
|
|
||||||
; nextln: return
|
; nextln: return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,3 +109,17 @@ ebb0(v0: i32, v999: i64):
|
|||||||
; nextln: $v2 = load.f32 $v1+0x7fff_ffff
|
; nextln: $v2 = load.f32 $v1+0x7fff_ffff
|
||||||
return v2
|
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),
|
ins=(src, dst),
|
||||||
other_side_effects=True)
|
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')
|
StackOffset = Operand('Offset', imm64, 'Offset from current stack pointer')
|
||||||
adjust_sp_imm = Instruction(
|
adjust_sp_imm = Instruction(
|
||||||
'adjust_sp_imm', r"""
|
'adjust_sp_imm', r"""
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ patterns that describe how base instructions can be transformed to other base
|
|||||||
instructions that are legal.
|
instructions that are legal.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import
|
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 instructions as insts
|
||||||
from . import types
|
from . import types
|
||||||
from .instructions import iadd, iadd_cout, iadd_cin, iadd_carry, iadd_imm
|
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.
|
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.
|
# Custom expansions for memory objects.
|
||||||
expand.custom_legalize(insts.global_addr, 'expand_global_addr')
|
expand.custom_legalize(insts.global_addr, 'expand_global_addr')
|
||||||
expand.custom_legalize(insts.heap_addr, 'expand_heap_addr')
|
expand.custom_legalize(insts.heap_addr, 'expand_heap_addr')
|
||||||
@@ -245,3 +254,20 @@ for ty, minus_zero in [
|
|||||||
a2 << band(y, b),
|
a2 << band(y, b),
|
||||||
a << bor(a1, a2)
|
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 settings as cfg
|
||||||
from . import instructions as x86
|
from . import instructions as x86
|
||||||
from .legalize import intel_expand
|
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 base.settings import allones_funcaddrs, is_pic
|
||||||
from .settings import use_sse41
|
from .settings import use_sse41
|
||||||
|
|
||||||
@@ -22,18 +22,18 @@ except ImportError:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
I32.legalize_monomorphic(expand)
|
I32.legalize_monomorphic(expand_flags)
|
||||||
I32.legalize_type(
|
I32.legalize_type(
|
||||||
default=narrow,
|
default=narrow,
|
||||||
b1=expand,
|
b1=expand_flags,
|
||||||
i32=intel_expand,
|
i32=intel_expand,
|
||||||
f32=intel_expand,
|
f32=intel_expand,
|
||||||
f64=intel_expand)
|
f64=intel_expand)
|
||||||
|
|
||||||
I64.legalize_monomorphic(expand)
|
I64.legalize_monomorphic(expand_flags)
|
||||||
I64.legalize_type(
|
I64.legalize_type(
|
||||||
default=narrow,
|
default=narrow,
|
||||||
b1=expand,
|
b1=expand_flags,
|
||||||
i32=intel_expand,
|
i32=intel_expand,
|
||||||
i64=intel_expand,
|
i64=intel_expand,
|
||||||
f32=intel_expand,
|
f32=intel_expand,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ intel_expand = XFormGroup(
|
|||||||
|
|
||||||
Use Intel-specific instructions if needed.
|
Use Intel-specific instructions if needed.
|
||||||
""",
|
""",
|
||||||
isa=ISA, chain=shared.expand)
|
isa=ISA, chain=shared.expand_flags)
|
||||||
|
|
||||||
a = Var('a')
|
a = Var('a')
|
||||||
dead = Var('dead')
|
dead = Var('dead')
|
||||||
|
|||||||
@@ -267,3 +267,39 @@ fn expand_fconst(
|
|||||||
};
|
};
|
||||||
pos.func.dfg.replace(inst).bitcast(ty, ival);
|
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