Author: Armin Rigo <[email protected]>
Branch:
Changeset: r85758:ef9006d99dbf
Date: 2016-07-18 16:09 +0200
http://bitbucket.org/pypy/pypy/changeset/ef9006d99dbf/
Log: Tweak the model, adding an explicit CHECK_MEMORY_ERROR resop that
aborts the loop with a MemoryError if its argument is null. Use it
on both GC and raw mallocs.
diff --git a/rpython/jit/backend/llsupport/llmodel.py
b/rpython/jit/backend/llsupport/llmodel.py
--- a/rpython/jit/backend/llsupport/llmodel.py
+++ b/rpython/jit/backend/llsupport/llmodel.py
@@ -408,8 +408,8 @@
deadframe = lltype.cast_opaque_ptr(jitframe.JITFRAMEPTR, deadframe)
descr = deadframe.jf_descr
res = history.AbstractDescr.show(self, descr)
- if not we_are_translated(): # for missing propagate_exception_descr
- if res is None:
+ if not we_are_translated(): # tests only: for missing
+ if res is None: # propagate_exception_descr
raise MissingLatestDescrError
assert isinstance(res, history.AbstractFailDescr)
return res
diff --git a/rpython/jit/backend/llsupport/rewrite.py
b/rpython/jit/backend/llsupport/rewrite.py
--- a/rpython/jit/backend/llsupport/rewrite.py
+++ b/rpython/jit/backend/llsupport/rewrite.py
@@ -26,7 +26,8 @@
class GcRewriterAssembler(object):
""" This class performs the following rewrites on the list of operations:
- - Turn all NEW_xxx to either a CALL_MALLOC_GC, or a CALL_MALLOC_NURSERY
+ - Turn all NEW_xxx to either a CALL_R/CHECK_MEMORY_ERROR,
+ or a CALL_MALLOC_NURSERY,
followed by SETFIELDs in order to initialize their GC fields. The
two advantages of CALL_MALLOC_NURSERY is that it inlines the common
path, and we need only one such operation to allocate several blocks
@@ -715,16 +716,17 @@
self._delayed_zero_setfields.clear()
def _gen_call_malloc_gc(self, args, v_result, descr):
- """Generate a CALL_MALLOC_GC with the given args."""
+ """Generate a CALL_R/CHECK_MEMORY_ERROR with the given args."""
self.emitting_an_operation_that_can_collect()
- op = ResOperation(rop.CALL_MALLOC_GC, args, descr=descr)
+ op = ResOperation(rop.CALL_R, args, descr=descr)
self.replace_op_with(v_result, op)
self.emit_op(op)
+ self.emit_op(ResOperation(rop.CHECK_MEMORY_ERROR, [op]))
# In general, don't add v_result to write_barrier_applied:
# v_result might be a large young array.
def gen_malloc_fixedsize(self, size, typeid, v_result):
- """Generate a CALL_MALLOC_GC(malloc_fixedsize_fn, ...).
+ """Generate a CALL_R(malloc_fixedsize_fn, ...).
Used on Boehm, and on the framework GC for large fixed-size
mallocs. (For all I know this latter case never occurs in
practice, but better safe than sorry.)
@@ -744,7 +746,7 @@
self.remember_write_barrier(v_result)
def gen_boehm_malloc_array(self, arraydescr, v_num_elem, v_result):
- """Generate a CALL_MALLOC_GC(malloc_array_fn, ...) for Boehm."""
+ """Generate a CALL_R(malloc_array_fn, ...) for Boehm."""
addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_array')
self._gen_call_malloc_gc([ConstInt(addr),
ConstInt(arraydescr.basesize),
@@ -755,7 +757,7 @@
self.gc_ll_descr.malloc_array_descr)
def gen_malloc_array(self, arraydescr, v_num_elem, v_result):
- """Generate a CALL_MALLOC_GC(malloc_array_fn, ...) going either
+ """Generate a CALL_R(malloc_array_fn, ...) going either
to the standard or the nonstandard version of the function."""
#
if (arraydescr.basesize == self.gc_ll_descr.standard_array_basesize
@@ -782,13 +784,13 @@
self._gen_call_malloc_gc(args, v_result, calldescr)
def gen_malloc_str(self, v_num_elem, v_result):
- """Generate a CALL_MALLOC_GC(malloc_str_fn, ...)."""
+ """Generate a CALL_R(malloc_str_fn, ...)."""
addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_str')
self._gen_call_malloc_gc([ConstInt(addr), v_num_elem], v_result,
self.gc_ll_descr.malloc_str_descr)
def gen_malloc_unicode(self, v_num_elem, v_result):
- """Generate a CALL_MALLOC_GC(malloc_unicode_fn, ...)."""
+ """Generate a CALL_R(malloc_unicode_fn, ...)."""
addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_unicode')
self._gen_call_malloc_gc([ConstInt(addr), v_num_elem], v_result,
self.gc_ll_descr.malloc_unicode_descr)
diff --git a/rpython/jit/backend/llsupport/test/test_rewrite.py
b/rpython/jit/backend/llsupport/test/test_rewrite.py
--- a/rpython/jit/backend/llsupport/test/test_rewrite.py
+++ b/rpython/jit/backend/llsupport/test/test_rewrite.py
@@ -254,8 +254,9 @@
jump()
""", """
[p1]
- p0 = call_malloc_gc(ConstClass(malloc_fixedsize), %(sdescr.size)d,\
- descr=malloc_fixedsize_descr)
+ p0 = call_r(ConstClass(malloc_fixedsize), %(sdescr.size)d,\
+ descr=malloc_fixedsize_descr)
+ check_memory_error(p0)
jump()
""")
@@ -267,10 +268,12 @@
jump()
""", """
[]
- p0 = call_malloc_gc(ConstClass(malloc_fixedsize), %(sdescr.size)d,\
- descr=malloc_fixedsize_descr)
- p1 = call_malloc_gc(ConstClass(malloc_fixedsize), %(sdescr.size)d,\
- descr=malloc_fixedsize_descr)
+ p0 = call_r(ConstClass(malloc_fixedsize), %(sdescr.size)d,\
+ descr=malloc_fixedsize_descr)
+ check_memory_error(p0)
+ p1 = call_r(ConstClass(malloc_fixedsize), %(sdescr.size)d,\
+ descr=malloc_fixedsize_descr)
+ check_memory_error(p1)
jump()
""")
@@ -281,16 +284,17 @@
jump()
""", """
[]
- p0 = call_malloc_gc(ConstClass(malloc_array), \
+ p0 = call_r(ConstClass(malloc_array), \
%(adescr.basesize)d, \
10, \
%(adescr.itemsize)d, \
%(adescr.lendescr.offset)d, \
descr=malloc_array_descr)
+ check_memory_error(p0)
jump()
""")
## should ideally be:
-## p0 = call_malloc_gc(ConstClass(malloc_fixedsize), \
+## p0 = call_r(ConstClass(malloc_fixedsize), \
## %(adescr.basesize + 10 * adescr.itemsize)d, \
## descr=malloc_fixedsize_descr)
## setfield_gc(p0, 10, descr=alendescr)
@@ -302,12 +306,13 @@
jump()
""", """
[i1]
- p0 = call_malloc_gc(ConstClass(malloc_array), \
+ p0 = call_r(ConstClass(malloc_array), \
%(adescr.basesize)d, \
i1, \
%(adescr.itemsize)d, \
%(adescr.lendescr.offset)d, \
descr=malloc_array_descr)
+ check_memory_error(p0)
jump()
""")
@@ -318,8 +323,9 @@
jump()
""", """
[p1]
- p0 = call_malloc_gc(ConstClass(malloc_fixedsize), 102, \
+ p0 = call_r(ConstClass(malloc_fixedsize), 102, \
descr=malloc_fixedsize_descr)
+ check_memory_error(p0)
gc_store(p0, 0, ConstClass(o_vtable), %(vtable_descr.field_size)s)
jump()
""")
@@ -331,12 +337,13 @@
jump()
""", """
[i1]
- p0 = call_malloc_gc(ConstClass(malloc_array), \
+ p0 = call_r(ConstClass(malloc_array), \
%(strdescr.basesize)d, \
i1, \
%(strdescr.itemsize)d, \
%(strlendescr.offset)d, \
descr=malloc_array_descr)
+ check_memory_error(p0)
jump()
""")
@@ -347,16 +354,17 @@
jump()
""", """
[i1]
- p0 = call_malloc_gc(ConstClass(malloc_array), \
+ p0 = call_r(ConstClass(malloc_array), \
%(unicodedescr.basesize)d, \
10, \
%(unicodedescr.itemsize)d, \
%(unicodelendescr.offset)d, \
descr=malloc_array_descr)
+ check_memory_error(p0)
jump()
""")
## should ideally be:
-## p0 = call_malloc_gc(ConstClass(malloc_fixedsize), \
+## p0 = call_r(ConstClass(malloc_fixedsize), \
## %(unicodedescr.basesize + \
## 10 * unicodedescr.itemsize)d, \
## descr=malloc_fixedsize_descr)
@@ -545,11 +553,12 @@
jump(i0)
""", """
[i0, p1]
- p0 = call_malloc_gc(ConstClass(malloc_array_nonstandard), \
+ p0 = call_r(ConstClass(malloc_array_nonstandard), \
64, 8, \
%(nonstd_descr.lendescr.offset)d, \
6464, i0, \
descr=malloc_array_nonstandard_descr)
+ check_memory_error(p0)
cond_call_gc_wb_array(p0, i0, descr=wbdescr)
gc_store_indexed(p0, i0, p1, 8, 64, 8)
jump(i0)
@@ -563,9 +572,10 @@
jump()
""", """
[]
- p0 = call_malloc_gc(ConstClass(malloc_array), 1, \
+ p0 = call_r(ConstClass(malloc_array), 1, \
%(bdescr.tid)d, 103, \
descr=malloc_array_descr)
+ check_memory_error(p0)
jump()
""")
@@ -601,9 +611,10 @@
jump()
""", """
[]
- p0 = call_malloc_gc(ConstClass(malloc_array), 1, \
+ p0 = call_r(ConstClass(malloc_array), 1, \
%(bdescr.tid)d, 20000000, \
descr=malloc_array_descr)
+ check_memory_error(p0)
jump()
""")
@@ -628,8 +639,9 @@
jump()
""", """
[p1]
- p0 = call_malloc_gc(ConstClass(malloc_big_fixedsize), 104, 9315, \
+ p0 = call_r(ConstClass(malloc_big_fixedsize), 104, 9315, \
descr=malloc_big_fixedsize_descr)
+ check_memory_error(p0)
gc_store(p0, 0, 0, %(vtable_descr.field_size)s)
jump()
""")
diff --git a/rpython/jit/backend/test/runner_test.py
b/rpython/jit/backend/test/runner_test.py
--- a/rpython/jit/backend/test/runner_test.py
+++ b/rpython/jit/backend/test/runner_test.py
@@ -4392,24 +4392,11 @@
'float', descr=calldescr)
assert longlong.getrealfloat(res) == expected
- def test_raw_malloc_out_of_memory(self):
- def failing_malloc(size):
- return 0
- effectinfo = EffectInfo([], [], [], [], [], [],
- EffectInfo.EF_CAN_RAISE,
- EffectInfo.OS_RAW_MALLOC_VARSIZE_CHAR)
- FPTR = self.Ptr(self.FuncType([lltype.Signed], lltype.Signed))
- func_ptr = llhelper(FPTR, failing_malloc)
- FUNC = deref(FPTR)
- funcbox = self.get_funcbox(self.cpu, func_ptr)
- calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- effectinfo)
- # Executing a CALL_I to the OS_RAW_MALLOC_VARSIZE_CHAR should
- # be special-cased so that a return value of 0 triggers a
- # get-out-of-loop MemoryError, like a failing CALL_MALLOC_GC
- py.test.raises(MissingLatestDescrError,
- self.execute_operation, rop.CALL_I,
- [funcbox, InputArgInt(12345)], 'void', descr=calldescr)
+ def test_check_memory_error(self):
+ self.execute_operation(
+ rop.CHECK_MEMORY_ERROR, [InputArgInt(12345)], 'void')
+ py.test.raises(MissingLatestDescrError, self.execute_operation,
+ rop.CHECK_MEMORY_ERROR, [InputArgInt(0)], 'void')
def test_compile_loop_with_target(self):
looptoken = JitCellToken()
diff --git a/rpython/jit/backend/x86/assembler.py
b/rpython/jit/backend/x86/assembler.py
--- a/rpython/jit/backend/x86/assembler.py
+++ b/rpython/jit/backend/x86/assembler.py
@@ -300,11 +300,7 @@
self._store_and_reset_exception(self.mc, eax)
ofs = self.cpu.get_ofs_of_frame_field('jf_guard_exc')
self.mc.MOV_br(ofs, eax.value)
- if not self.cpu.propagate_exception_descr:
- # for tests, or non-translated
- propagate_exception_descr = 0
- else:
- propagate_exception_descr = rffi.cast(lltype.Signed,
+ propagate_exception_descr = rffi.cast(lltype.Signed,
cast_instance_to_gcref(self.cpu.propagate_exception_descr))
ofs = self.cpu.get_ofs_of_frame_field('jf_descr')
self.mc.MOV(RawEbpLoc(ofs), imm(propagate_exception_descr))
@@ -1520,15 +1516,9 @@
# ----------
- def genop_call_malloc_gc(self, op, arglocs, result_loc):
- self._genop_call(op, arglocs, result_loc)
- self.propagate_memoryerror_if_eax_is_null()
-
- def propagate_memoryerror_if_eax_is_null(self):
- # if self.propagate_exception_path == 0 (tests), this may jump to 0
- # and segfaults. too bad. the alternative is to continue anyway
- # with eax==0, but that will segfault too.
- self.mc.TEST_rr(eax.value, eax.value)
+ def genop_discard_check_memory_error(self, op, arglocs):
+ reg = arglocs[0]
+ self.mc.TEST(reg, reg)
if WORD == 4:
self.mc.J_il(rx86.Conditions['Z'], self.propagate_exception_path)
self.mc.add_pending_relocation()
diff --git a/rpython/jit/backend/x86/regalloc.py
b/rpython/jit/backend/x86/regalloc.py
--- a/rpython/jit/backend/x86/regalloc.py
+++ b/rpython/jit/backend/x86/regalloc.py
@@ -884,8 +884,6 @@
if oopspecindex == EffectInfo.OS_MATH_READ_TIMESTAMP:
return self._consider_math_read_timestamp(op)
self._consider_call(op)
- if oopspecindex == EffectInfo.OS_RAW_MALLOC_VARSIZE_CHAR:
- self.assembler.propagate_memoryerror_if_eax_is_null()
consider_call_i = _consider_real_call
consider_call_r = _consider_real_call
consider_call_f = _consider_real_call
@@ -904,9 +902,10 @@
consider_call_release_gil_i = _consider_call_release_gil
consider_call_release_gil_f = _consider_call_release_gil
consider_call_release_gil_n = _consider_call_release_gil
-
- def consider_call_malloc_gc(self, op):
- self._consider_call(op)
+
+ def consider_check_memory_error(self, op):
+ x = self.rm.make_sure_var_in_reg(op.getarg(0))
+ self.perform_discard(op, [x])
def _consider_call_assembler(self, op):
locs = self.locs_for_call_assembler(op)
diff --git a/rpython/jit/metainterp/executor.py
b/rpython/jit/metainterp/executor.py
--- a/rpython/jit/metainterp/executor.py
+++ b/rpython/jit/metainterp/executor.py
@@ -379,7 +379,7 @@
rop.CALL_RELEASE_GIL_F,
rop.CALL_RELEASE_GIL_N,
rop.QUASIIMMUT_FIELD,
- rop.CALL_MALLOC_GC,
+ rop.CHECK_MEMORY_ERROR,
rop.CALL_MALLOC_NURSERY,
rop.CALL_MALLOC_NURSERY_VARSIZE,
rop.CALL_MALLOC_NURSERY_VARSIZE_FRAME,
diff --git a/rpython/jit/metainterp/optimizeopt/heap.py
b/rpython/jit/metainterp/optimizeopt/heap.py
--- a/rpython/jit/metainterp/optimizeopt/heap.py
+++ b/rpython/jit/metainterp/optimizeopt/heap.py
@@ -345,7 +345,8 @@
opnum == rop.ENTER_PORTAL_FRAME or # no effect whatsoever
opnum == rop.LEAVE_PORTAL_FRAME or # no effect whatsoever
opnum == rop.COPYSTRCONTENT or # no effect on GC struct/array
- opnum == rop.COPYUNICODECONTENT): # no effect on GC struct/array
+ opnum == rop.COPYUNICODECONTENT or # no effect on GC struct/array
+ opnum == rop.CHECK_MEMORY_ERROR): # may only abort the whole
loop
return
if rop.is_call(op.opnum):
if rop.is_call_assembler(op.getopnum()):
diff --git a/rpython/jit/metainterp/optimizeopt/info.py
b/rpython/jit/metainterp/optimizeopt/info.py
--- a/rpython/jit/metainterp/optimizeopt/info.py
+++ b/rpython/jit/metainterp/optimizeopt/info.py
@@ -400,6 +400,12 @@
def _force_elements(self, op, optforce, descr):
self.size = -1
+ # at this point we have just written the
+ # 'op = CALL_I(..., OS_RAW_MALLOC_VARSIZE_CHAR)'.
+ # Emit now a CHECK_MEMORY_ERROR resop.
+ check_op = ResOperation(rop.CHECK_MEMORY_ERROR, [op])
+ optforce.emit_operation(check_op)
+ #
buffer = self._get_buffer()
for i in range(len(buffer.offsets)):
# write the value
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -1818,6 +1818,7 @@
[i1]
label(i1)
i2 = call_i('malloc', 20, descr=raw_malloc_descr)
+ check_memory_error(i2)
raw_store(i2, 0, i1, descr=rawarraydescr_char)
raw_store(i2, 1, 123, descr=rawarraydescr_char)
raw_store(i2, 2, 456, descr=rawarraydescr_char)
@@ -1843,6 +1844,7 @@
[i1]
label(i1)
i2 = call_i('malloc', 10, descr=raw_malloc_descr)
+ check_memory_error(i2)
raw_store(i2, 0, i1, descr=rawarraydescr)
setarrayitem_raw(i2, 2, 456, descr=rawarraydescr_char)
call_n('free', i2, descr=raw_free_descr)
@@ -1865,6 +1867,7 @@
[i1]
label(i1)
i2 = call_i('malloc', 10, descr=raw_malloc_descr)
+ check_memory_error(i2)
raw_store(i2, 0, i1, descr=rawarraydescr)
i3 = getarrayitem_raw_i(i2, 0, descr=rawarraydescr_char)
call_n('free', i2, descr=raw_free_descr)
@@ -1927,6 +1930,7 @@
label(i0, i1)
# these ops are generated by VirtualRawBufferValue._really_force
i2 = call_i('malloc', 10, descr=raw_malloc_descr)
+ check_memory_error(i2)
raw_store(i2, 0, 42, descr=rawarraydescr_char)
raw_store(i2, 5, 4242, descr=rawarraydescr_char)
# this is generated by VirtualRawSliceValue._really_force
@@ -1955,6 +1959,7 @@
call_n('free', i0, descr=raw_free_descr)
label(i2)
i3 = call_i('malloc', 10, descr=raw_malloc_descr)
+ check_memory_error(i3)
raw_store(i3, 0, i2, descr=rawarraydescr)
jump(i3)
"""
@@ -2027,6 +2032,7 @@
expected = """
[f1]
i0 = call_i('malloc', 16, descr=raw_malloc_descr)
+ check_memory_error(i0)
escape_n(i0)
i1 = int_add(i0, 8)
setarrayitem_raw(i1, 0, f1, descr=rawarraydescr_float)
@@ -8797,14 +8803,22 @@
ops = """
[i1]
i0 = call_i(123, 10, descr=raw_malloc_descr)
+ guard_no_exception() []
jump(i0)
"""
- self.optimize_loop(ops, ops)
+ expected = """
+ [i1]
+ i0 = call_i(123, 10, descr=raw_malloc_descr)
+ check_memory_error(i0)
+ jump(i0)
+ """
+ self.optimize_loop(ops, expected)
def test_raw_buffer_int_is_true(self):
ops = """
[iinp]
i0 = call_i(123, 10, descr=raw_malloc_descr)
+ guard_no_exception() []
i1 = int_is_true(i0)
guard_true(i1) []
i2 = int_is_zero(i0)
@@ -8814,6 +8828,7 @@
expected = """
[i2]
i0 = call_i(123, 10, descr=raw_malloc_descr)
+ check_memory_error(i0)
jump(i0)
"""
self.optimize_loop(ops, expected)
@@ -8872,6 +8887,7 @@
ops = """
[i0]
i = call_i('malloc', 10, descr=raw_malloc_descr)
+ guard_no_exception() []
is = int_add(i, 8)
escape_n(i)
i1 = int_add(i0, 1)
@@ -8883,6 +8899,7 @@
expected = """
[i0]
i = call_i('malloc', 10, descr=raw_malloc_descr)
+ check_memory_error(i)
escape_n(i)
i1 = int_add(i0, 1)
i2 = int_lt(i1, 100)
@@ -8950,6 +8967,7 @@
ops = """
[i0, p0]
i2 = call_i('malloc', 10, descr=raw_malloc_descr)
+ guard_no_exception() []
setarrayitem_raw(i2, 0, 13, descr=rawarraydescr)
setfield_gc(p0, i2, descr=valuedescr)
i1 = int_add(i0, 1)
@@ -8971,12 +8989,20 @@
ops = """
[]
i2 = call_i('malloc', 10, descr=raw_malloc_descr)
+ guard_no_exception() []
guard_value(i2, 12345) []
jump()
"""
+ expected = """
+ []
+ i2 = call_i('malloc', 10, descr=raw_malloc_descr)
+ check_memory_error(i2)
+ guard_value(i2, 12345) []
+ jump()
+ """
# getting InvalidLoop would be a good idea, too.
# (this test was written to show it would previously crash)
- self.optimize_loop(ops, ops)
+ self.optimize_loop(ops, expected)
def test_unroll_constant_null_1(self):
ops = """
diff --git a/rpython/jit/metainterp/resoperation.py
b/rpython/jit/metainterp/resoperation.py
--- a/rpython/jit/metainterp/resoperation.py
+++ b/rpython/jit/metainterp/resoperation.py
@@ -1157,7 +1157,7 @@
'CALL_RELEASE_GIL/*d/fin',
# release the GIL and "close the stack" for asmgcc
'CALL_PURE/*d/rfin', # removed before it's passed to the
backend
- 'CALL_MALLOC_GC/*d/r', # like CALL, but NULL => propagate MemoryError
+ 'CHECK_MEMORY_ERROR/1/n', # after a CALL: NULL => propagate MemoryError
'CALL_MALLOC_NURSERY/1/r', # nursery malloc, const number of bytes, zeroed
'CALL_MALLOC_NURSERY_VARSIZE/3d/r',
'CALL_MALLOC_NURSERY_VARSIZE_FRAME/1/r',
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit