diff --git a/cranelift/docs/langref.rst b/cranelift/docs/langref.rst index b7fe64b924..4001cf3688 100644 --- a/cranelift/docs/langref.rst +++ b/cranelift/docs/langref.rst @@ -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 ---------------- diff --git a/cranelift/filetests/isa/intel/legalize-custom.cton b/cranelift/filetests/isa/intel/legalize-custom.cton index 0327e0a317..41411ec21a 100644 --- a/cranelift/filetests/isa/intel/legalize-custom.cton +++ b/cranelift/filetests/isa/intel/legalize-custom.cton @@ -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 } diff --git a/cranelift/filetests/isa/intel/legalize-memory.cton b/cranelift/filetests/isa/intel/legalize-memory.cton index fc17cfd2dc..1cdcf680ba 100644 --- a/cranelift/filetests/isa/intel/legalize-memory.cton +++ b/cranelift/filetests/isa/intel/legalize-memory.cton @@ -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 +} diff --git a/lib/cretonne/meta/base/instructions.py b/lib/cretonne/meta/base/instructions.py index b6ab96a16e..89fd36812d 100644 --- a/lib/cretonne/meta/base/instructions.py +++ b/lib/cretonne/meta/base/instructions.py @@ -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""" diff --git a/lib/cretonne/meta/base/legalize.py b/lib/cretonne/meta/base/legalize.py index ebcda3fee1..b809708072 100644 --- a/lib/cretonne/meta/base/legalize.py +++ b/lib/cretonne/meta/base/legalize.py @@ -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) + )) diff --git a/lib/cretonne/meta/isa/intel/encodings.py b/lib/cretonne/meta/isa/intel/encodings.py index 1e1eff820c..80614cb8b4 100644 --- a/lib/cretonne/meta/isa/intel/encodings.py +++ b/lib/cretonne/meta/isa/intel/encodings.py @@ -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, diff --git a/lib/cretonne/meta/isa/intel/legalize.py b/lib/cretonne/meta/isa/intel/legalize.py index 70a8e9166b..f6e5b1dc9d 100644 --- a/lib/cretonne/meta/isa/intel/legalize.py +++ b/lib/cretonne/meta/isa/intel/legalize.py @@ -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') diff --git a/lib/cretonne/src/legalizer/mod.rs b/lib/cretonne/src/legalizer/mod.rs index 565f3e0971..81dde2f457 100644 --- a/lib/cretonne/src/legalizer/mod.rs +++ b/lib/cretonne/src/legalizer/mod.rs @@ -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, + ); +}