Skip to content

Commit 49b08a3

Browse files
authored
expand memoryrefnew capabilities (#58768)
The goal here is 2-fold. Firstly, this should let us simplify the boundscheck (not yet implimented), but this also should reduce Julia IR side a bit.
1 parent d7bdd21 commit 49b08a3

File tree

9 files changed

+92
-22
lines changed

9 files changed

+92
-22
lines changed

Compiler/src/optimize.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,9 @@ function iscall_with_boundscheck(@nospecialize(stmt), sv::PostOptAnalysisState)
720720
f === nothing && return false
721721
if f === getfield
722722
nargs = 4
723-
elseif f === memoryrefnew || f === memoryrefget || f === memoryref_isassigned
723+
elseif f === memoryrefnew
724+
nargs= 3
725+
elseif f === memoryrefget || f === memoryref_isassigned
724726
nargs = 4
725727
elseif f === memoryrefset!
726728
nargs = 5

Compiler/src/tfuncs.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2096,6 +2096,7 @@ end
20962096
@nospecs function memoryref_tfunc(𝕃::AbstractLattice, ref, idx, boundscheck)
20972097
memoryref_builtin_common_errorcheck(ref, Const(:not_atomic), boundscheck) || return Bottom
20982098
hasintersect(widenconst(idx), Int) || return Bottom
2099+
hasintersect(widenconst(ref), GenericMemory) && return memoryref_tfunc(𝕃, ref)
20992100
return ref
21002101
end
21012102
add_tfunc(memoryrefnew, 1, 3, memoryref_tfunc, 1)
@@ -2107,7 +2108,7 @@ end
21072108
add_tfunc(memoryrefoffset, 1, 1, memoryrefoffset_tfunc, 5)
21082109

21092110
@nospecs function memoryref_builtin_common_errorcheck(mem, order, boundscheck)
2110-
hasintersect(widenconst(mem), GenericMemoryRef) || return false
2111+
hasintersect(widenconst(mem), Union{GenericMemory, GenericMemoryRef}) || return false
21112112
hasintersect(widenconst(order), Symbol) || return false
21122113
hasintersect(widenconst(unwrapva(boundscheck)), Bool) || return false
21132114
return true
@@ -2203,7 +2204,7 @@ function memoryref_builtin_common_nothrow(argtypes::Vector{Any})
22032204
idx = widenconst(argtypes[2])
22042205
idx Int || return false
22052206
boundscheck Bool || return false
2206-
memtype GenericMemoryRef || return false
2207+
memtype Union{GenericMemory, GenericMemoryRef} || return false
22072208
# If we have @inbounds (last argument is false), we're allowed to assume
22082209
# we don't throw bounds errors.
22092210
if isa(boundscheck, Const)

Compiler/test/inference.jl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1597,9 +1597,12 @@ let memoryref_tfunc(@nospecialize xs...) = Compiler.memoryref_tfunc(Compiler.fal
15971597
@test memoryref_tfunc(MemoryRef{Int}, Int, Symbol) == Union{}
15981598
@test memoryref_tfunc(MemoryRef{Int}, Int, Bool) == MemoryRef{Int}
15991599
@test memoryref_tfunc(MemoryRef{Int}, Int, Vararg{Bool}) == MemoryRef{Int}
1600-
@test memoryref_tfunc(Memory{Int}, Int) == Union{}
1601-
@test memoryref_tfunc(Any, Any, Any) == Any # also probably could be GenericMemoryRef
1602-
@test memoryref_tfunc(Any, Any) == Any # also probably could be GenericMemoryRef
1600+
@test memoryref_tfunc(Memory{Int}, Int) == MemoryRef{Int}
1601+
@test memoryref_tfunc(Memory{Int}, Int, Symbol) == Union{}
1602+
@test memoryref_tfunc(Memory{Int}, Int, Bool) == MemoryRef{Int}
1603+
@test memoryref_tfunc(Memory{Int}, Int, Vararg{Bool}) == MemoryRef{Int}
1604+
@test memoryref_tfunc(Any, Any, Any) == GenericMemoryRef
1605+
@test memoryref_tfunc(Any, Any) == GenericMemoryRef
16031606
@test memoryref_tfunc(Any) == GenericMemoryRef
16041607
@test memoryrefget_tfunc(MemoryRef{Int}, Symbol, Bool) === Int
16051608
@test memoryrefget_tfunc(MemoryRef{Int}, Any, Any) === Int

base/boot.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ const undef = UndefInitializer()
592592

593593
# memoryref is simply convenience wrapper function around memoryrefnew
594594
memoryref(mem::GenericMemory) = memoryrefnew(mem)
595-
memoryref(mem::GenericMemory, i::Integer) = memoryrefnew(memoryrefnew(mem), Int(i), @_boundscheck)
595+
memoryref(mem::GenericMemory, i::Integer) = memoryrefnew(mem, Int(i), @_boundscheck)
596596
memoryref(ref::GenericMemoryRef, i::Integer) = memoryrefnew(ref, Int(i), @_boundscheck)
597597
GenericMemoryRef(mem::GenericMemory) = memoryref(mem)
598598
GenericMemoryRef(mem::GenericMemory, i::Integer) = memoryref(mem, i)

base/essentials.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ default_access_order(a::GenericMemoryRef{:atomic}) = :monotonic
395395
function getindex(A::GenericMemory, i::Int)
396396
@_noub_if_noinbounds_meta
397397
(@_boundscheck) && checkbounds(A, i)
398-
memoryrefget(memoryrefnew(memoryrefnew(A), i, false), default_access_order(A), false)
398+
memoryrefget(memoryrefnew(A, i, false), default_access_order(A), false)
399399
end
400400

401401
getindex(A::GenericMemoryRef) = memoryrefget(A, default_access_order(A), @_boundscheck)
@@ -973,7 +973,7 @@ function setindex!(A::Array{Any}, @nospecialize(x), i::Int)
973973
memoryrefset!(memoryrefnew(getfield(A, :ref), i, false), x, :not_atomic, false)
974974
return A
975975
end
976-
setindex!(A::Memory{Any}, @nospecialize(x), i::Int) = (memoryrefset!(memoryrefnew(memoryrefnew(A), i, @_boundscheck), x, :not_atomic, @_boundscheck); A)
976+
setindex!(A::Memory{Any}, @nospecialize(x), i::Int) = (memoryrefset!(memoryrefnew(A, i, @_boundscheck), x, :not_atomic, @_boundscheck); A)
977977
setindex!(A::MemoryRef{T}, x) where {T} = (memoryrefset!(A, convert(T, x), :not_atomic, @_boundscheck); A)
978978
setindex!(A::MemoryRef{Any}, @nospecialize(x)) = (memoryrefset!(A, x, :not_atomic, @_boundscheck); A)
979979

src/builtins.c

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1765,30 +1765,45 @@ JL_CALLABLE(jl_f_memoryrefnew)
17651765
return (jl_value_t*)jl_new_memoryref(typ, m, m->ptr);
17661766
}
17671767
else {
1768-
JL_TYPECHK(memoryrefnew, genericmemoryref, args[0]);
17691768
JL_TYPECHK(memoryrefnew, long, args[1]);
17701769
if (nargs == 3)
17711770
JL_TYPECHK(memoryrefnew, bool, args[2]);
1771+
size_t i = (size_t) jl_unbox_long(args[1]) - 1;
1772+
char *data;
1773+
if (jl_is_genericmemory(args[0])) {
1774+
jl_genericmemory_t *m = (jl_genericmemory_t*)args[0];
1775+
jl_value_t *typ = jl_apply_type((jl_value_t*)jl_genericmemoryref_type, jl_svec_data(((jl_datatype_t*)jl_typetagof(m))->parameters), 3);
1776+
JL_GC_PROMISE_ROOTED(typ); // it is a concrete type
1777+
if (i >= m->length)
1778+
jl_bounds_error((jl_value_t*)m, args[1]);
1779+
const jl_datatype_layout_t *layout = ((jl_datatype_t*)jl_typetagof(m))->layout;
1780+
if (layout->flags.arrayelem_isunion || layout->size == 0)
1781+
return (jl_value_t*)jl_new_memoryref(typ, m, (char*)i);
1782+
else if (layout->flags.arrayelem_isboxed)
1783+
return (jl_value_t*)jl_new_memoryref(typ, m, (char*)m->ptr + sizeof(jl_value_t*)*i);
1784+
return (jl_value_t*)jl_new_memoryref(typ, m, (char*)m->ptr + layout->size*i);
1785+
}
1786+
JL_TYPECHK(memoryrefnew, genericmemoryref, args[0]);
17721787
jl_genericmemoryref_t *m = (jl_genericmemoryref_t*)args[0];
1773-
size_t i = jl_unbox_long(args[1]) - 1;
1774-
const jl_datatype_layout_t *layout = ((jl_datatype_t*)jl_typetagof(m->mem))->layout;
1775-
char *data = (char*)m->ptr_or_offset;
1788+
jl_genericmemory_t *mem = m->mem;
1789+
data = (char*)m->ptr_or_offset;
1790+
const jl_datatype_layout_t *layout = ((jl_datatype_t*)jl_typetagof(mem))->layout;
17761791
if (layout->flags.arrayelem_isboxed) {
1777-
if (((data - (char*)m->mem->ptr) / sizeof(jl_value_t*)) + i >= m->mem->length)
1792+
if (((data - (char*)mem->ptr) / sizeof(jl_value_t*)) + i >= mem->length)
17781793
jl_bounds_error((jl_value_t*)m, args[1]);
17791794
data += sizeof(jl_value_t*) * i;
17801795
}
17811796
else if (layout->flags.arrayelem_isunion || layout->size == 0) {
1782-
if ((size_t)data + i >= m->mem->length)
1797+
if ((size_t)data + i >= mem->length)
17831798
jl_bounds_error((jl_value_t*)m, args[1]);
17841799
data += i;
17851800
}
17861801
else {
1787-
if (((data - (char*)m->mem->ptr) / layout->size) + i >= m->mem->length)
1802+
if (((data - (char*)mem->ptr) / layout->size) + i >= mem->length)
17881803
jl_bounds_error((jl_value_t*)m, args[1]);
17891804
data += layout->size * i;
17901805
}
1791-
return (jl_value_t*)jl_new_memoryref((jl_value_t*)jl_typetagof(m), m->mem, data);
1806+
return (jl_value_t*)jl_new_memoryref((jl_value_t*)jl_typetagof(m), mem, data);
17921807
}
17931808
}
17941809

src/cgutils.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4684,6 +4684,45 @@ static jl_cgval_t _emit_memoryref(jl_codectx_t &ctx, const jl_cgval_t &mem, cons
46844684
return _emit_memoryref(ctx, boxed(ctx, mem), data, layout, typ);
46854685
}
46864686

4687+
static jl_cgval_t emit_memoryref_direct(jl_codectx_t &ctx, const jl_cgval_t &mem, jl_cgval_t idx, jl_value_t *typ, jl_value_t *inbounds, const jl_datatype_layout_t *layout)
4688+
{
4689+
bool isboxed = layout->flags.arrayelem_isboxed;
4690+
bool isunion = layout->flags.arrayelem_isunion;
4691+
bool isghost = layout->size == 0;
4692+
Value *boxmem = boxed(ctx, mem);
4693+
Value *i = emit_unbox(ctx, ctx.types().T_size, idx, (jl_value_t*)jl_long_type);
4694+
Value *idx0 = ctx.builder.CreateSub(i, ConstantInt::get(ctx.types().T_size, 1));
4695+
bool bc = bounds_check_enabled(ctx, inbounds);
4696+
if (bc) {
4697+
BasicBlock *failBB, *endBB;
4698+
failBB = BasicBlock::Create(ctx.builder.getContext(), "oob");
4699+
endBB = BasicBlock::Create(ctx.builder.getContext(), "idxend");
4700+
Value *mlen = emit_genericmemorylen(ctx, boxmem, typ);
4701+
Value *inbound = ctx.builder.CreateICmpULT(idx0, mlen);
4702+
setName(ctx.emission_context, inbound, "memoryref_isinbounds");
4703+
ctx.builder.CreateCondBr(inbound, endBB, failBB);
4704+
failBB->insertInto(ctx.f);
4705+
ctx.builder.SetInsertPoint(failBB);
4706+
ctx.builder.CreateCall(prepare_call(jlboundserror_func),
4707+
{ mark_callee_rooted(ctx, boxmem), i });
4708+
ctx.builder.CreateUnreachable();
4709+
endBB->insertInto(ctx.f);
4710+
ctx.builder.SetInsertPoint(endBB);
4711+
}
4712+
Value *data;
4713+
4714+
if ((!isboxed && isunion) || isghost) {
4715+
data = idx0;
4716+
4717+
} else {
4718+
data = emit_genericmemoryptr(ctx, boxmem, layout, 0);
4719+
Type *elty = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, jl_tparam1(typ));
4720+
data = ctx.builder.CreateInBoundsGEP(elty, data, idx0);
4721+
}
4722+
4723+
return _emit_memoryref(ctx, boxmem, data, layout, typ);
4724+
}
4725+
46874726
static Value *emit_memoryref_FCA(jl_codectx_t &ctx, const jl_cgval_t &ref, const jl_datatype_layout_t *layout)
46884727
{
46894728
if (!ref.inline_roots.empty()) {

src/codegen.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4138,16 +4138,25 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f,
41384138

41394139
else if (f == BUILTIN(memoryrefnew) && (nargs == 2 || nargs == 3)) {
41404140
const jl_cgval_t &ref = argv[1];
4141-
jl_value_t *mty_dt = jl_unwrap_unionall(ref.typ);
4142-
if (jl_is_genericmemoryref_type(mty_dt) && jl_is_concrete_type(mty_dt)) {
4143-
mty_dt = jl_field_type_concrete((jl_datatype_t*)mty_dt, 1);
4144-
const jl_datatype_layout_t *layout = ((jl_datatype_t*)mty_dt)->layout;
4141+
jl_datatype_t *mty_dt = (jl_datatype_t*)jl_unwrap_unionall(ref.typ);
4142+
if (jl_is_genericmemoryref_type(mty_dt) && jl_is_concrete_type((jl_value_t*)mty_dt)) {
4143+
mty_dt = (jl_datatype_t*)jl_field_type_concrete(mty_dt, 1);
4144+
const jl_datatype_layout_t *layout = mty_dt->layout;
41454145
jl_value_t *boundscheck = nargs == 3 ? argv[3].constant : nullptr;
41464146
if (nargs == 3)
41474147
emit_typecheck(ctx, argv[3], (jl_value_t*)jl_bool_type, "memoryrefnew");
41484148
*ret = emit_memoryref(ctx, ref, argv[2], boundscheck, layout);
41494149
return true;
41504150
}
4151+
if (jl_is_genericmemory_type(mty_dt) && jl_is_concrete_type((jl_value_t*)mty_dt)) {
4152+
const jl_datatype_layout_t *layout = mty_dt->layout;
4153+
jl_value_t *boundscheck = nargs == 3 ? argv[3].constant : nullptr;
4154+
if (nargs == 3)
4155+
emit_typecheck(ctx, argv[3], (jl_value_t*)jl_bool_type, "memoryrefnew");
4156+
jl_value_t *typ = jl_apply_type((jl_value_t*)jl_genericmemoryref_type, jl_svec_data(mty_dt->parameters), jl_svec_len(mty_dt->parameters));
4157+
*ret = emit_memoryref_direct(ctx, ref, argv[2], typ, boundscheck, layout);
4158+
return true;
4159+
}
41514160
}
41524161

41534162
else if (f == BUILTIN(memoryrefoffset) && nargs == 1) {

stdlib/REPL/test/precompilation.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ if !Sys.iswindows()
3333
# given this test checks that startup is snappy, it's best to add workloads to
3434
# contrib/generate_precompile.jl rather than increase this number. But if that's not
3535
# possible, it'd be helpful to add a comment with the statement and a reason below
36-
expected_precompiles = 0
36+
expected_precompiles = 1
37+
# Jameson told me to bump this
3738

3839
n_precompiles = count(r"precompile\(", tracecompile_out)
3940

0 commit comments

Comments
 (0)