Skip to content

Commit c48cde4

Browse files
committed
tests
1 parent 232cc9c commit c48cde4

1 file changed

Lines changed: 151 additions & 0 deletions

File tree

zjit/src/hir/tests.rs

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2093,6 +2093,157 @@ pub(crate) mod hir_build_tests {
20932093
");
20942094
}
20952095

2096+
#[test]
2097+
fn test_send_reloads_referenced_block_param() {
2098+
eval("
2099+
def take(x) = x
2100+
def consume = yield
2101+
def test(&block)
2102+
consume { take(block) }
2103+
block
2104+
end
2105+
test { 1 }
2106+
");
2107+
assert_contains_opcode("test", YARVINSN_send);
2108+
// The block reads `block` (passed as a regular argument), so it references the
2109+
// block param. `getblockparam` is recorded as a read, but reading the block param
2110+
// materializes the captured block into its slot, so the block param must be
2111+
// reloaded after the call (this is the lazy_load_hooks miscompile scenario).
2112+
assert_snapshot!(hir_string("test"), @"
2113+
fn test@<compiled>:5:
2114+
bb1():
2115+
EntryPoint interpreter
2116+
v1:BasicObject = LoadSelf
2117+
v2:CPtr = LoadSP
2118+
v3:BasicObject = LoadField v2, :block@0x1000
2119+
Jump bb3(v1, v3)
2120+
bb2():
2121+
EntryPoint JIT(0)
2122+
v6:BasicObject = LoadArg :self@0
2123+
v7:BasicObject = LoadArg :block@1
2124+
Jump bb3(v6, v7)
2125+
bb3(v9:BasicObject, v10:BasicObject):
2126+
v15:BasicObject = Send v9, 0x1008, :consume # SendFallbackReason: Uncategorized(send)
2127+
PatchPoint NoEPEscape(test)
2128+
v18:CPtr = LoadSP
2129+
v19:BasicObject = LoadField v18, :block@0x1000
2130+
v24:CPtr = GetEP 0
2131+
v25:CUInt64 = LoadField v24, :VM_ENV_DATA_INDEX_FLAGS@0x1030
2132+
v26:CBool = IsBlockParamModified v25
2133+
CondBranch v26, bb4(), bb5()
2134+
bb4():
2135+
v28:BasicObject = LoadField v24, :block@0x1031
2136+
Jump bb6(v28)
2137+
bb5():
2138+
v30:BasicObject = GetBlockParam :block, l0, EP@3
2139+
Jump bb6(v30)
2140+
bb6(v23:BasicObject):
2141+
CheckInterrupts
2142+
Return v23
2143+
");
2144+
}
2145+
2146+
#[test]
2147+
fn test_send_does_not_reload_unreferenced_block_param() {
2148+
eval("
2149+
def consume = yield
2150+
def test(&block)
2151+
a = 1
2152+
consume { a }
2153+
block
2154+
end
2155+
test { 1 }
2156+
");
2157+
assert_contains_opcode("test", YARVINSN_send);
2158+
// The block only references `a`, never the block param, so the block param
2159+
// cannot have been materialized by the call and is not reloaded. (Before the
2160+
// reload filter was refined, the block param was reloaded after every
2161+
// send-with-block, even when the block could not have touched it.)
2162+
assert_snapshot!(hir_string("test"), @"
2163+
fn test@<compiled>:4:
2164+
bb1():
2165+
EntryPoint interpreter
2166+
v1:BasicObject = LoadSelf
2167+
v2:CPtr = LoadSP
2168+
v3:BasicObject = LoadField v2, :block@0x1000
2169+
v4:NilClass = Const Value(nil)
2170+
Jump bb3(v1, v3, v4)
2171+
bb2():
2172+
EntryPoint JIT(0)
2173+
v7:BasicObject = LoadArg :self@0
2174+
v8:BasicObject = LoadArg :block@1
2175+
v9:NilClass = Const Value(nil)
2176+
Jump bb3(v7, v8, v9)
2177+
bb3(v11:BasicObject, v12:BasicObject, v13:NilClass):
2178+
v17:Fixnum[1] = Const Value(1)
2179+
v22:BasicObject = Send v11, 0x1008, :consume # SendFallbackReason: Uncategorized(send)
2180+
PatchPoint NoEPEscape(test)
2181+
v29:CPtr = GetEP 0
2182+
v30:CUInt64 = LoadField v29, :VM_ENV_DATA_INDEX_FLAGS@0x1030
2183+
v31:CBool = IsBlockParamModified v30
2184+
CondBranch v31, bb4(), bb5()
2185+
bb4():
2186+
v33:BasicObject = LoadField v29, :block@0x1031
2187+
Jump bb6(v33)
2188+
bb5():
2189+
v35:BasicObject = GetBlockParam :block, l0, EP@4
2190+
Jump bb6(v35)
2191+
bb6(v28:BasicObject):
2192+
CheckInterrupts
2193+
Return v28
2194+
");
2195+
}
2196+
2197+
#[test]
2198+
fn test_send_with_anonymous_block_param() {
2199+
eval("
2200+
def consume = yield
2201+
def test(&)
2202+
consume { consume(&) }
2203+
consume(&)
2204+
end
2205+
test { 1 }
2206+
");
2207+
assert_contains_opcode("test", YARVINSN_send);
2208+
// An anonymous `&` block param can only be forwarded with `&`, which compiles to
2209+
// `getblockparamproxy` and reads the block from the EP. It never materializes the
2210+
// param into its slot, so the block param is read directly from the EP after the
2211+
// call and is not reloaded -- there is nothing a reload could recover.
2212+
assert_snapshot!(hir_string("test"), @"
2213+
fn test@<compiled>:4:
2214+
bb1():
2215+
EntryPoint interpreter
2216+
v1:BasicObject = LoadSelf
2217+
v2:CPtr = LoadSP
2218+
v3:BasicObject = LoadField v2, :&@0x1000
2219+
Jump bb3(v1, v3)
2220+
bb2():
2221+
EntryPoint JIT(0)
2222+
v6:BasicObject = LoadArg :self@0
2223+
v7:BasicObject = LoadArg :&@1
2224+
Jump bb3(v6, v7)
2225+
bb3(v9:BasicObject, v10:BasicObject):
2226+
v15:BasicObject = Send v9, 0x1008, :consume # SendFallbackReason: Uncategorized(send)
2227+
PatchPoint NoEPEscape(test)
2228+
v24:CPtr = GetEP 0
2229+
v25:CUInt64 = LoadField v24, :VM_ENV_DATA_INDEX_FLAGS@0x1030
2230+
v26:CBool = IsBlockParamModified v25
2231+
CondBranch v26, bb4(), bb5()
2232+
bb4():
2233+
v28:BasicObject = LoadField v24, :&@0x1031
2234+
Jump bb6(v28, v28)
2235+
bb5():
2236+
v30:CInt64 = LoadField v24, :VM_ENV_DATA_INDEX_SPECVAL@0x1032
2237+
v31:CInt64 = GuardAnyBitSet v30, CUInt64(1) recompile
2238+
v32:ObjectSubclass[BlockParamProxy] = Const Value(VALUE(0x1038))
2239+
Jump bb6(v32, v10)
2240+
bb6(v22:BasicObject, v23:BasicObject):
2241+
v35:BasicObject = Send v9, &block, :consume, v22 # SendFallbackReason: Uncategorized(send)
2242+
CheckInterrupts
2243+
Return v35
2244+
");
2245+
}
2246+
20962247
#[test]
20972248
fn test_send_reloads_local_written_by_nested_block() {
20982249
eval("

0 commit comments

Comments
 (0)