Skip to content

Commit a4561b0

Browse files
KenoIan Atol
authored andcommitted
Begin rebase of 42465 onto 43888
1 parent 325f414 commit a4561b0

23 files changed

+311
-28
lines changed

base/array.jl

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,20 @@ function vect(X...)
147147
return copyto!(Vector{T}(undef, length(X)), X)
148148
end
149149

150-
size(a::Array, d::Integer) = arraysize(a, convert(Int, d))
151-
size(a::Vector) = (arraysize(a,1),)
152-
size(a::Matrix) = (arraysize(a,1), arraysize(a,2))
153-
size(a::Array{<:Any,N}) where {N} = (@inline; ntuple(M -> size(a, M), Val(N))::Dims)
150+
const ImmutableArray = Core.ImmutableArray
151+
const IMArray{T,N} = Union{Array{T, N}, ImmutableArray{T,N}}
152+
const IMVector{T} = IMArray{T, 1}
153+
const IMMatrix{T} = IMArray{T, 2}
154154

155-
asize_from(a::Array, n) = n > ndims(a) ? () : (arraysize(a,n), asize_from(a, n+1)...)
155+
ImmutableArray(a::Array) = Core.arrayfreeze(a)
156+
Array(a::ImmutableArray) = Core.arraythaw(a)
157+
158+
size(a::IMArray, d::Integer) = arraysize(a, convert(Int, d))
159+
size(a::IMVector) = (arraysize(a,1),)
160+
size(a::IMMatrix) = (arraysize(a,1), arraysize(a,2))
161+
size(a::IMArray{<:Any,N}) where {N} = (@_inline_meta; ntuple(M -> size(a, M), Val(N))::Dims)
162+
163+
asize_from(a::IMArray, n) = n > ndims(a) ? () : (arraysize(a,n), asize_from(a, n+1)...)
156164

157165
allocatedinline(T::Type) = (@_pure_meta; ccall(:jl_stored_inline, Cint, (Any,), T) != Cint(0))
158166

@@ -223,6 +231,13 @@ function isassigned(a::Array, i::Int...)
223231
ccall(:jl_array_isassigned, Cint, (Any, UInt), a, ii) == 1
224232
end
225233

234+
function isassigned(a::ImmutableArray, i::Int...)
235+
@_inline_meta
236+
ii = (_sub2ind(size(a), i...) % UInt) - 1
237+
@boundscheck ii < length(a) % UInt || return false
238+
ccall(:jl_array_isassigned, Cint, (Any, UInt), a, ii) == 1
239+
end
240+
226241
## copy ##
227242

228243
"""
@@ -921,7 +936,10 @@ function getindex end
921936
@eval getindex(A::Array, i1::Int) = arrayref($(Expr(:boundscheck)), A, i1)
922937
@eval getindex(A::Array, i1::Int, i2::Int, I::Int...) = (@inline; arrayref($(Expr(:boundscheck)), A, i1, i2, I...))
923938

924-
# Faster contiguous indexing using copyto! for AbstractUnitRange and Colon
939+
@eval getindex(A::ImmutableArray, i1::Int) = arrayref($(Expr(:boundscheck)), A, i1)
940+
@eval getindex(A::ImmutableArray, i1::Int, i2::Int, I::Int...) = (@_inline_meta; arrayref($(Expr(:boundscheck)), A, i1, i2, I...))
941+
942+
# Faster contiguous indexing using copyto! for UnitRange and Colon
925943
function getindex(A::Array, I::AbstractUnitRange{<:Integer})
926944
@inline
927945
@boundscheck checkbounds(A, I)

base/compiler/ssair/ir.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,13 @@ function setindex!(x::IRCode, repl::Instruction, s::SSAValue)
319319
return x
320320
end
321321

322+
function ssadominates(ir::IRCode, domtree::DomTree, ssa1::Int, ssa2::Int)
323+
bb1 = block_for_inst(ir.cfg, ssa1)
324+
bb2 = block_for_inst(ir.cfg, ssa2)
325+
bb1 == bb2 && return ssa1 < ssa2
326+
return dominates(domtree, bb1, bb2)
327+
end
328+
322329
# SSA values that need renaming
323330
struct OldSSAValue
324331
id::Int

base/compiler/ssair/passes.jl

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1760,3 +1760,77 @@ function cfg_simplify!(ir::IRCode)
17601760
compact.active_result_bb = length(bb_starts)
17611761
return finish(compact)
17621762
end
1763+
1764+
function is_allocation(stmt)
1765+
isexpr(stmt, :foreigncall) || return false
1766+
s = stmt.args[1]
1767+
isa(s, QuoteNode) && (s = s.value)
1768+
return s === :jl_alloc_array_1d
1769+
end
1770+
1771+
function memory_opt!(ir::IRCode)
1772+
compact = IncrementalCompact(ir, false)
1773+
uses = IdDict{Int, Vector{Int}}()
1774+
relevant = IdSet{Int}()
1775+
revisit = Int[]
1776+
function mark_val(val)
1777+
isa(val, SSAValue) || return
1778+
val.id in relevant && pop!(relevant, val.id)
1779+
end
1780+
for ((_, idx), stmt) in compact
1781+
if isa(stmt, ReturnNode)
1782+
isdefined(stmt, :val) || continue
1783+
val = stmt.val
1784+
if isa(val, SSAValue) && val.id in relevant
1785+
(haskey(uses, val.id)) || (uses[val.id] = Int[])
1786+
push!(uses[val.id], idx)
1787+
end
1788+
continue
1789+
end
1790+
(isexpr(stmt, :call) || isexpr(stmt, :foreigncall)) || continue
1791+
if is_allocation(stmt)
1792+
push!(relevant, idx)
1793+
# TODO: Mark everything else here
1794+
continue
1795+
end
1796+
# TODO: Replace this by interprocedural escape analysis
1797+
if is_known_call(stmt, arrayset, compact)
1798+
# The value being set escapes, everything else doesn't
1799+
mark_val(stmt.args[4])
1800+
arr = stmt.args[3]
1801+
if isa(arr, SSAValue) && arr.id in relevant
1802+
(haskey(uses, arr.id)) || (uses[arr.id] = Int[])
1803+
push!(uses[arr.id], idx)
1804+
end
1805+
elseif is_known_call(stmt, Core.arrayfreeze, compact) && isa(stmt.args[2], SSAValue)
1806+
push!(revisit, idx)
1807+
else
1808+
# For now we assume everything escapes
1809+
# TODO: We could handle PhiNodes specially and improve this
1810+
for ur in userefs(stmt)
1811+
mark_val(ur[])
1812+
end
1813+
end
1814+
end
1815+
ir = finish(compact)
1816+
isempty(revisit) && return ir
1817+
domtree = construct_domtree(ir.cfg.blocks)
1818+
for idx in revisit
1819+
# Make sure that the value we reference didn't escape
1820+
id = ir.stmts[idx][:inst].args[2].id
1821+
(id in relevant) || continue
1822+
1823+
# We're ok to steal the memory if we don't dominate any uses
1824+
ok = true
1825+
for use in uses[id]
1826+
if ssadominates(ir, domtree, idx, use)
1827+
ok = false
1828+
break
1829+
end
1830+
end
1831+
ok || continue
1832+
1833+
ir.stmts[idx][:inst].args[1] = Core.mutating_arrayfreeze
1834+
end
1835+
return ir
1836+
end

base/compiler/tfuncs.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1803,6 +1803,21 @@ function builtin_tfunction(interp::AbstractInterpreter, @nospecialize(f), argtyp
18031803
sv::Union{InferenceState,Nothing})
18041804
if f === tuple
18051805
return tuple_tfunc(argtypes)
1806+
elseif f === Core.arrayfreeze || f === Core.arraythaw
1807+
if length(argtypes) != 1
1808+
isva && return Any
1809+
return Bottom
1810+
end
1811+
a = widenconst(argtypes[1])
1812+
at = (f === Core.arrayfreeze ? Array : ImmutableArray)
1813+
rt = (f === Core.arrayfreeze ? ImmutableArray : Array)
1814+
if a <: at
1815+
unw = unwrap_unionall(a)
1816+
if isa(unw, DataType)
1817+
return rewrap_unionall(rt{unw.parameters[1], unw.parameters[2]}, a)
1818+
end
1819+
end
1820+
return rt
18061821
end
18071822
if isa(f, IntrinsicFunction)
18081823
if is_pure_intrinsic_infer(f) && _all(@nospecialize(a) -> isa(a, Const), argtypes)

base/dict.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ end
373373
function setindex!(h::Dict{K,V}, v0, key0) where V where K
374374
key = convert(K, key0)
375375
if !isequal(key, key0)
376-
throw(ArgumentError("$(limitrepr(key0)) is not a valid key for type $K"))
376+
throw(KeyTypeError(K, key0))
377377
end
378378
setindex!(h, v0, key)
379379
end

base/experimental.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ module Experimental
1111

1212
using Base: Threads, sync_varname
1313
using Base.Meta
14+
using Base: ImmutableArray
15+
1416

1517
"""
1618
Const(A::Array)

src/builtin_proto.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ DECLARE_BUILTIN(_typebody);
5555
DECLARE_BUILTIN(typeof);
5656
DECLARE_BUILTIN(_typevar);
5757
DECLARE_BUILTIN(donotdelete);
58+
DECLARE_BUILTIN(arrayfreeze);
59+
DECLARE_BUILTIN(arraythaw);
60+
DECLARE_BUILTIN(mutating_arrayfreeze);
5861

5962
JL_CALLABLE(jl_f_invoke_kwsorter);
6063
#ifdef DEFINE_BUILTIN_GLOBALS

src/builtins.c

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,7 +1373,9 @@ JL_CALLABLE(jl_f__typevar)
13731373
JL_CALLABLE(jl_f_arraysize)
13741374
{
13751375
JL_NARGS(arraysize, 2, 2);
1376-
JL_TYPECHK(arraysize, array, args[0]);
1376+
if (!jl_is_arrayish(args[0])) {
1377+
jl_type_error("arraysize", (jl_value_t*)jl_array_type, args[0]);
1378+
}
13771379
jl_array_t *a = (jl_array_t*)args[0];
13781380
size_t nd = jl_array_ndims(a);
13791381
JL_TYPECHK(arraysize, long, args[1]);
@@ -1412,7 +1414,9 @@ JL_CALLABLE(jl_f_arrayref)
14121414
{
14131415
JL_NARGSV(arrayref, 3);
14141416
JL_TYPECHK(arrayref, bool, args[0]);
1415-
JL_TYPECHK(arrayref, array, args[1]);
1417+
if (!jl_is_arrayish(args[1])) {
1418+
jl_type_error("arrayref", (jl_value_t*)jl_array_type, args[1]);
1419+
}
14161420
jl_array_t *a = (jl_array_t*)args[1];
14171421
size_t i = array_nd_index(a, &args[2], nargs - 2, "arrayref");
14181422
return jl_arrayref(a, i);
@@ -1735,6 +1739,54 @@ JL_CALLABLE(jl_f_set_binding_type)
17351739
return jl_nothing;
17361740
}
17371741

1742+
JL_CALLABLE(jl_f_arrayfreeze)
1743+
{
1744+
JL_NARGSV(arrayfreeze, 1);
1745+
JL_TYPECHK(arrayfreeze, array, args[0]);
1746+
jl_array_t *a = (jl_array_t*)args[0];
1747+
jl_datatype_t *it = (jl_datatype_t *)jl_apply_type2((jl_value_t*)jl_immutable_array_type,
1748+
jl_tparam0(jl_typeof(a)), jl_tparam1(jl_typeof(a)));
1749+
JL_GC_PUSH1(&it);
1750+
// The idea is to elide this copy if the compiler or runtime can prove that
1751+
// doing so is safe to do.
1752+
jl_array_t *na = jl_array_copy(a);
1753+
jl_set_typeof(na, it);
1754+
JL_GC_POP();
1755+
return (jl_value_t*)na;
1756+
}
1757+
1758+
JL_CALLABLE(jl_f_mutating_arrayfreeze)
1759+
{
1760+
// N.B.: These error checks pretend to be arrayfreeze since this is a drop
1761+
// in replacement and we don't want to change the visible error type in the
1762+
// optimizer
1763+
JL_NARGSV(arrayfreeze, 1);
1764+
JL_TYPECHK(arrayfreeze, array, args[0]);
1765+
jl_array_t *a = (jl_array_t*)args[0];
1766+
jl_datatype_t *it = (jl_datatype_t *)jl_apply_type2((jl_value_t*)jl_immutable_array_type,
1767+
jl_tparam0(jl_typeof(a)), jl_tparam1(jl_typeof(a)));
1768+
jl_set_typeof(a, it);
1769+
return (jl_value_t*)a;
1770+
}
1771+
1772+
JL_CALLABLE(jl_f_arraythaw)
1773+
{
1774+
JL_NARGSV(arraythaw, 1);
1775+
if (((jl_datatype_t*)jl_typeof(args[0]))->name != jl_immutable_array_typename) {
1776+
jl_type_error("arraythaw", (jl_value_t*)jl_immutable_array_type, args[0]);
1777+
}
1778+
jl_array_t *a = (jl_array_t*)args[0];
1779+
jl_datatype_t *it = (jl_datatype_t *)jl_apply_type2((jl_value_t*)jl_array_type,
1780+
jl_tparam0(jl_typeof(a)), jl_tparam1(jl_typeof(a)));
1781+
JL_GC_PUSH1(&it);
1782+
// The idea is to elide this copy if the compiler or runtime can prove that
1783+
// doing so is safe to do.
1784+
jl_array_t *na = jl_array_copy(a);
1785+
jl_set_typeof(na, it);
1786+
JL_GC_POP();
1787+
return (jl_value_t*)na;
1788+
}
1789+
17381790
// IntrinsicFunctions ---------------------------------------------------------
17391791

17401792
static void (*runtime_fp[num_intrinsics])(void);
@@ -1890,6 +1942,10 @@ void jl_init_primitives(void) JL_GC_DISABLED
18901942
jl_builtin_arrayset = add_builtin_func("arrayset", jl_f_arrayset);
18911943
jl_builtin_arraysize = add_builtin_func("arraysize", jl_f_arraysize);
18921944

1945+
jl_builtin_arrayfreeze = add_builtin_func("arrayfreeze", jl_f_arrayfreeze);
1946+
jl_builtin_mutating_arrayfreeze = add_builtin_func("mutating_arrayfreeze", jl_f_mutating_arrayfreeze);
1947+
jl_builtin_arraythaw = add_builtin_func("arraythaw", jl_f_arraythaw);
1948+
18931949
// method table utils
18941950
jl_builtin_applicable = add_builtin_func("applicable", jl_f_applicable);
18951951
jl_builtin_invoke = add_builtin_func("invoke", jl_f_invoke);
@@ -1965,6 +2021,7 @@ void jl_init_primitives(void) JL_GC_DISABLED
19652021
add_builtin("AbstractArray", (jl_value_t*)jl_abstractarray_type);
19662022
add_builtin("DenseArray", (jl_value_t*)jl_densearray_type);
19672023
add_builtin("Array", (jl_value_t*)jl_array_type);
2024+
add_builtin("ImmutableArray", (jl_value_t*)jl_immutable_array_type);
19682025

19692026
add_builtin("Expr", (jl_value_t*)jl_expr_type);
19702027
add_builtin("LineNumberNode", (jl_value_t*)jl_linenumbernode_type);

src/cgutils.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ static Type *_julia_type_to_llvm(jl_codegen_params_t *ctx, LLVMContext &ctxt, jl
491491
if (isboxed) *isboxed = false;
492492
if (jt == (jl_value_t*)jl_bottom_type)
493493
return getVoidTy(ctxt);
494-
if (jl_is_concrete_immutable(jt)) {
494+
if (jl_is_concrete_immutable(jt) && !jl_is_arrayish_type(jt)) {
495495
if (jl_datatype_nbits(jt) == 0)
496496
return getVoidTy(ctxt);
497497
Type *t = _julia_struct_to_llvm(ctx, ctxt, jt, isboxed);

src/codegen.cpp

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,15 @@ static const auto pointer_from_objref_func = new JuliaFunction{
10541054
Attributes(C, {Attribute::NonNull}),
10551055
None); },
10561056
};
1057+
static const auto mutating_arrayfreeze_func = new JuliaFunction{
1058+
"julia.mutating_arrayfreeze",
1059+
[](LLVMContext &C) { return FunctionType::get(T_prjlvalue,
1060+
{T_prjlvalue, T_prjlvalue}, false); },
1061+
[](LLVMContext &C) { return AttributeList::get(C,
1062+
Attributes(C, {Attribute::NoUnwind, Attribute::NoRecurse}),
1063+
Attributes(C, {Attribute::NonNull}),
1064+
None); },
1065+
};
10571066

10581067
static const auto jltuple_func = new JuliaFunction{XSTR(jl_f_tuple), get_func_sig, get_func_attrs};
10591068
static std::map<jl_fptr_args_t, JuliaFunction*> builtin_func_map;
@@ -1125,7 +1134,7 @@ static bool deserves_retbox(jl_value_t* t)
11251134
static bool deserves_sret(jl_value_t *dt, Type *T)
11261135
{
11271136
assert(jl_is_datatype(dt));
1128-
return (size_t)jl_datatype_size(dt) > sizeof(void*) && !T->isFloatingPointTy() && !T->isVectorTy();
1137+
return (size_t)jl_datatype_size(dt) > sizeof(void*) && !T->isFloatingPointTy() && !T->isVectorTy() && !jl_is_arrayish_type(dt);
11291138
}
11301139

11311140

@@ -2996,6 +3005,21 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f,
29963005
}
29973006
}
29983007

3008+
else if (f == jl_builtin_mutating_arrayfreeze && nargs == 1) {
3009+
const jl_cgval_t &ary = argv[1];
3010+
jl_value_t *aty_dt = jl_unwrap_unionall(ary.typ);
3011+
if (jl_is_array_type(aty_dt)) {
3012+
jl_datatype_t *it = (jl_datatype_t *)jl_apply_type2((jl_value_t*)jl_immutable_array_type,
3013+
jl_tparam0(aty_dt), jl_tparam1(aty_dt));
3014+
*ret = mark_julia_type(ctx,
3015+
ctx.builder.CreateCall(prepare_call(mutating_arrayfreeze_func),
3016+
{ boxed(ctx, ary),
3017+
track_pjlvalue(ctx, literal_pointer_val(ctx, (jl_value_t*)it)) }), true, it);
3018+
return true;
3019+
}
3020+
return false;
3021+
}
3022+
29993023
else if (f == jl_builtin_arrayset && nargs >= 4) {
30003024
const jl_cgval_t &ary = argv[2];
30013025
jl_cgval_t val = argv[3];
@@ -8176,7 +8200,10 @@ extern "C" void jl_init_llvm(void)
81768200
{ jl_f_arrayset_addr, new JuliaFunction{XSTR(jl_f_arrayset), get_func_sig, get_func_attrs} },
81778201
{ jl_f_arraysize_addr, new JuliaFunction{XSTR(jl_f_arraysize), get_func_sig, get_func_attrs} },
81788202
{ jl_f_apply_type_addr, new JuliaFunction{XSTR(jl_f_apply_type), get_func_sig, get_func_attrs} },
8179-
{ jl_f_donotdelete_addr, new JuliaFunction{XSTR(jl_f_donotdelete), get_func_sig, get_donotdelete_func_attrs} }
8203+
{ jl_f_donotdelete_addr, new JuliaFunction{XSTR(jl_f_donotdelete), get_func_sig, get_donotdelete_func_attrs} },
8204+
{ jl_f_arrayfreeze_addr, new JuliaFunction{XSTR(jl_f_arrayfreeze), get_func_sig, get_func_attrs} },
8205+
{ jl_f_arraythaw_addr, new JuliaFunction{XSTR(jl_f_arraythaw), get_func_sig, get_func_attrs} },
8206+
{ jl_f_mutating_arrayfreeze_addr, new JuliaFunction{XSTR(jl_f_mutating_arrayfreeze), get_func_sig, get_func_attrs} }
81808207
};
81818208

81828209
jl_default_debug_info_kind = (int) DICompileUnit::DebugEmissionKind::FullDebug;

0 commit comments

Comments
 (0)