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:
Jakob Stoklund Olesen
2018-02-12 13:50:22 -08:00
parent a73fcb2691
commit 3ccc3f4f9b
8 changed files with 107 additions and 13 deletions

View File

@@ -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
----------------

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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"""

View File

@@ -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)
))

View File

@@ -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,

View File

@@ -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')

View File

@@ -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,
);
}