@@ -1256,26 +1256,47 @@ function cfg_simplify!(ir::IRCode)
12561256 return finish (compact)
12571257end
12581258
1259+ # function is_known_fcall(stmt::Expr, @nospecialize(func))
1260+ # isexpr(stmt, :foreigncall) || return false
1261+ # s = stmt.args[1]
1262+ # isa(s, QuoteNode) && (s = s.value)
1263+ # return s === func
1264+ # end
1265+
1266+ function is_known_fcall (stmt:: Expr , funcs:: Vector{Symbol} )
1267+ isexpr (stmt, :foreigncall ) || return false
1268+ s = stmt. args[1 ]
1269+ isa (s, QuoteNode) && (s = s. value)
1270+ # return any(e -> s === e, funcs)
1271+ return true in map (e -> s === e, funcs)
1272+ end
1273+
12591274function is_allocation (stmt:: Expr )
12601275 isexpr (stmt, :foreigncall ) || return false
12611276 s = stmt. args[1 ]
12621277 isa (s, QuoteNode) && (s = s. value)
1263- return (s === :jl_alloc_array_1d || s === :jl_alloc_array_2d || s === :jl_alloc_array_3d || s === :jl_new_array )
1278+ return (s === :jl_alloc_array_1d
1279+ || s === :jl_alloc_array_2d
1280+ || s === :jl_alloc_array_3d
1281+ || s === :jl_new_array )
12641282end
12651283
12661284function memory_opt! (ir:: IRCode )
12671285 compact = IncrementalCompact (ir, false )
12681286 uses = IdDict {Int, Vector{Int}} ()
1269- relevant = IdSet {Int} () # allocations
1270- revisit = Int[] # potential targets for mutating_arrayfreeze
1287+ relevant = IdSet {Int} () # allocations and their sizes
1288+ revisit = Int[] # potential targets for a mutating_arrayfreeze drop-in
12711289
12721290 function mark_escape (@nospecialize val)
12731291 isa (val, SSAValue) || return
1292+ # println(val.id, " escaped.")
12741293 val. id in relevant && pop! (relevant, val. id)
12751294 end
12761295
12771296 for ((_, idx), stmt) in compact
12781297
1298+ # println("idx: ", idx, " = ", stmt)
1299+
12791300 if isa (stmt, ReturnNode)
12801301 isdefined (stmt, :val ) || continue
12811302 val = stmt. val
@@ -1284,6 +1305,26 @@ function memory_opt!(ir::IRCode)
12841305 push! (uses[val. id], idx)
12851306 end
12861307 continue
1308+
1309+ # check for phinodes that are possibly allocations
1310+ elseif isa (stmt, PhiNode)
1311+
1312+ # this loop seems like a waste, but using map here didn't go well
1313+ defined = true
1314+ for i = 1 : length (stmt. values)
1315+ if ! isassigned (stmt. values, i)
1316+ defined = false
1317+ end
1318+ end
1319+
1320+ defined || continue
1321+
1322+ for val in stmt. values
1323+ if isa (val, SSAValue) && val. id in relevant
1324+ # println("Adding ", idx ," to relevant from PhiNode: " , stmt)
1325+ push! (relevant, idx)
1326+ end
1327+ end
12871328 end
12881329
12891330 (isexpr (stmt, :call ) || isexpr (stmt, :foreigncall )) || continue
@@ -1294,54 +1335,101 @@ function memory_opt!(ir::IRCode)
12941335 continue
12951336 end
12961337
1297- # TODO : Replace this by interprocedural escape analysis
1298- if is_known_call (stmt, arrayset, compact)
1299- # arrayset expr.args:
1300- # :(Base.arrayset)
1301- # false
1302- # :(%2) array
1303- # :(%8) value
1304- # :(%7) index
1338+ if is_known_call (stmt, arrayset, compact) && length (stmt. args) >= 5
13051339 # The value being set escapes, everything else doesn't
1306- (length (stmt. args) == 5 ) || continue # fix boundserror during precompile --- but how do we have arrayset with < 5 args?
13071340 mark_escape (stmt. args[4 ])
1341+
13081342 arr = stmt. args[3 ]
1343+
13091344 if isa (arr, SSAValue) && arr. id in relevant
13101345 (haskey (uses, arr. id)) || (uses[arr. id] = Int[])
13111346 push! (uses[arr. id], idx)
13121347 end
13131348
1314- elseif is_known_call (stmt, Core . arrayfreeze , compact) && isa (stmt. args[ 2 ], SSAValue)
1315- push! (revisit, idx)
1349+ elseif is_known_call (stmt, arrayref , compact) && length (stmt. args) == 4
1350+ arr = stmt . args[ 3 ]
13161351
1317- elseif is_known_call (stmt, arraysize, compact) && isa (stmt . args[ 2 ] , SSAValue) && isa (stmt . args[ 3 ], Number)
1318- arr = stmt . args[ 2 ]
1319- dim = stmt . args[ 3 ]
1320- typ = types (compact)[arr]
1352+ if isa (arr , SSAValue) && arr . id in relevant
1353+ ( haskey (uses, arr. id)) || (uses[arr . id] = Int[])
1354+ push! (uses[arr . id], idx)
1355+ end
13211356
1322- while ! isa (typ, Type)
1323- typ = typeof (typ)
1357+ elseif is_known_call (stmt, setindex!, compact) && length (stmt. args) == 4
1358+ # println("setindex!: ", stmt.args)
1359+ # handle similarly to arrayset
1360+ # escape the value being set
1361+ val = stmt. args[3 ]
1362+ mark_escape (val)
1363+ # track usage of arr for dominance analysis
1364+ arr = stmt. args[2 ]
1365+ if isa (arr, SSAValue) && arr. id in relevant
1366+ (haskey (uses, arr. id)) || (uses[arr. id] = Int[])
1367+ push! (uses[arr. id], idx)
13241368 end
13251369
1326- if isa (typ, Core. Const)
1327- typ = typ. val
1370+ # these foreigncalls have similar structure and don't escape our array, so handle them all at once
1371+ elseif is_known_fcall (stmt, [:jl_array_ptr , :jl_array_copy ]) && length (stmt. args) == 6
1372+ # println("is_known_fcall: ", stmt)
1373+ arr = stmt. args[6 ]
1374+
1375+ # just record usage info
1376+ if isa (arr, SSAValue) && arr. id in relevant
1377+ (haskey (uses, arr. id)) || (uses[arr. id] = Int[])
1378+ push! (uses[arr. id], idx)
13281379 end
13291380
1330- # make sure this call isn't going to throw
1331- if typ <: AbstractArray && dim >= 1
1332- # don't escape the array, but mark usage for dom analysis
1333- if arr. id in relevant
1334- (haskey (uses, arr. id)) || (uses[arr. id] = Int[])
1335- push! (uses[arr. id], idx)
1336- end
1337- else # if this call throws or we can't tell, the array definitely escapes
1338- for ur in userefs (stmt)
1339- mark_escape (ur[])
1340- end
1381+ # elseif is_known_fcall(stmt, :jl_array_ptr) && length(stmt.args) == 6
1382+ # arr = stmt.args[6]
1383+
1384+ # if isa(arr, SSAValue) && arr.id in relevant
1385+ # (haskey(uses, arr.id)) || (uses[arr.id] = Int[])
1386+ # push!(uses[arr.id], idx)
1387+ # end
1388+
1389+ # elseif is_known_fcall(stmt, :jl_array_copy) && length(stmt.args) == 6
1390+ # arr = stmt.args[6]
1391+
1392+ # if isa(arr, SSAValue) && arr.id in relevant
1393+ # (haskey(uses, arr.id)) || (uses[arr.id] = Int[])
1394+ # push!(uses[arr.id], idx)
1395+ # end
1396+
1397+ elseif is_known_call (stmt, arraysize, compact) && isa (stmt. args[2 ], SSAValue) # && isa(stmt.args[3], Number)
1398+ arr = stmt. args[2 ]
1399+ # dim = stmt.args[3]
1400+ # typ = types(compact)[arr]
1401+
1402+ # if isa(typ, Core.Const)
1403+ # typ = typ.val
1404+ # end
1405+
1406+ # NEW: since exceptions no longer escape arrays, we can just assume no escape
1407+
1408+ if arr. id in relevant
1409+ (haskey (uses, arr. id)) || (uses[arr. id] = Int[])
1410+ push! (uses[arr. id], idx)
13411411 end
1412+
1413+ # # make sure this call isn't going to throw
1414+ # if isa(typ, Type) && typ <: AbstractArray && dim >= 1
1415+ # # don't escape the array, but mark usage for dom analysis
1416+ # if arr.id in relevant
1417+ # (haskey(uses, arr.id)) || (uses[arr.id] = Int[])
1418+ # push!(uses[arr.id], idx)
1419+ # end
1420+
1421+ # else # if this call throws or we can't tell, assume all uses escape
1422+ # for ur in userefs(stmt)
1423+ # mark_escape(ur[])
1424+ # end
1425+ # end
1426+
1427+ elseif is_known_call (stmt, Core. arrayfreeze, compact) && isa (stmt. args[2 ], SSAValue)
1428+ # mark these for potential replacement with mutating_arrayfreeze
1429+ push! (revisit, idx)
1430+
13421431 else
1343- # For now we assume everything escapes
1344- # TODO : We could handle PhiNodes specially and improve this
1432+ # Assume everything else escapes
13451433 for ur in userefs (stmt)
13461434 mark_escape (ur[])
13471435 end
@@ -1357,8 +1445,18 @@ function memory_opt!(ir::IRCode)
13571445 # Make sure that the value we reference didn't escape
13581446 stmt = ir. stmts[idx][:inst ]:: Expr
13591447 id = (stmt. args[2 ]:: SSAValue ). id
1448+ # print("Relevant: ")
1449+ # for id in relevant
1450+ # print(id, " ")
1451+ # end
1452+ # println(" ")
1453+ # print("Revisit: ")
1454+ # Main.Base.show(revisit)
1455+ # println()
13601456 (id in relevant) || continue
13611457
1458+ # println("Revisiting ", stmt)
1459+
13621460 # We're ok to steal the memory if we don't dominate any uses
13631461 ok = true
13641462 if haskey (uses, id)
@@ -1370,6 +1468,7 @@ function memory_opt!(ir::IRCode)
13701468 end
13711469 end
13721470 ok || continue
1471+ # println("Optimization of ", stmt)
13731472 stmt. args[1 ] = Core. mutating_arrayfreeze
13741473 end
13751474 return ir
0 commit comments