Skip to content

Commit e003251

Browse files
gbaraldioscarddssmith
authored andcommitted
make memorynew intrinsic
Co-authored-by: Jameson Nash <[email protected]> Co-authored-by: Jeff Bezanson <[email protected]> Co-authored-by: Gabriel Baraldi <[email protected]>
1 parent fe26173 commit e003251

22 files changed

+431
-108
lines changed

Compiler/src/tfuncs.jl

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2017,6 +2017,12 @@ function tuple_tfunc(𝕃::AbstractLattice, argtypes::Vector{Any})
20172017
return anyinfo ? PartialStruct(𝕃, typ, argtypes) : typ
20182018
end
20192019

2020+
@nospecs function memorynew_tfunc(𝕃::AbstractLattice, memtype, m)
2021+
hasintersect(widenconst(m), Int) || return Bottom
2022+
return tmeet(𝕃, instanceof_tfunc(memtype, true)[1], GenericMemory)
2023+
end
2024+
add_tfunc(Core.memorynew, 2, 2, memorynew_tfunc, 10)
2025+
20202026
@nospecs function memoryrefget_tfunc(𝕃::AbstractLattice, mem, order, boundscheck)
20212027
memoryref_builtin_common_errorcheck(mem, order, boundscheck) || return Bottom
20222028
return memoryref_elemtype(mem)
@@ -2244,7 +2250,16 @@ function _builtin_nothrow(𝕃::AbstractLattice, @nospecialize(f::Builtin), argt
22442250
@nospecialize(rt))
22452251
= partialorder(𝕃)
22462252
na = length(argtypes)
2247-
if f === memoryrefnew
2253+
if f === Core.memorynew
2254+
argtypes[1] isa Const && argtypes[2] isa Const || return false
2255+
MemT = argtypes[1].val
2256+
isconcretetype(MemT) && MemT <: GenericMemory || return false
2257+
len = argtypes[2].val
2258+
len isa Int && 0 <= len < typemax(Int) || return false
2259+
elsz = datatype_layoutsize(MemT)
2260+
checked_smul_int(len, elsz)[2] && return false
2261+
return true
2262+
elseif f === memoryrefnew
22482263
return memoryref_builtin_common_nothrow(argtypes)
22492264
elseif f === memoryrefoffset
22502265
length(argtypes) == 1 || return false
@@ -2347,6 +2362,7 @@ const _EFFECT_FREE_BUILTINS = [
23472362
isa,
23482363
UnionAll,
23492364
getfield,
2365+
Core.memorynew,
23502366
memoryrefnew,
23512367
memoryrefoffset,
23522368
memoryrefget,
@@ -2381,6 +2397,7 @@ const _INACCESSIBLEMEM_BUILTINS = Any[
23812397
compilerbarrier,
23822398
Core._typevar,
23832399
donotdelete,
2400+
Core.memorynew,
23842401
]
23852402

23862403
const _ARGMEM_BUILTINS = Any[
@@ -2543,7 +2560,7 @@ function builtin_effects(𝕃::AbstractLattice, @nospecialize(f::Builtin), argty
25432560
consistent = ALWAYS_TRUE
25442561
elseif f === memoryrefget || f === memoryrefset! || f === memoryref_isassigned
25452562
consistent = CONSISTENT_IF_INACCESSIBLEMEMONLY
2546-
elseif f === Core._typevar
2563+
elseif f === Core._typevar || f === Core.memorynew
25472564
consistent = CONSISTENT_IF_NOTRETURNED
25482565
else
25492566
consistent = ALWAYS_FALSE

Compiler/test/irpasses.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,12 +1083,13 @@ end
10831083
# test `flags_for_effects` and DCE
10841084
# ================================
10851085

1086-
let # effect-freeness computation for array allocation
1086+
@testset "effect-freeness computation for array allocation" begin
10871087

10881088
# should eliminate dead allocations
10891089
good_dims = [1, 2, 3, 4, 10]
10901090
Ns = [1, 2, 3, 4, 10]
1091-
for dim = good_dims, N = Ns
1091+
Ts = Any[Int, Union{Missing,Nothing}, Nothing, Any]
1092+
@testset "$dim, $N" for dim in good_dims, N in Ns
10921093
Int64(dim)^N > typemax(Int) && continue
10931094
dims = ntuple(i->dim, N)
10941095
@test @eval fully_eliminated() do
@@ -1099,7 +1100,7 @@ let # effect-freeness computation for array allocation
10991100

11001101
# shouldn't eliminate erroneous dead allocations
11011102
bad_dims = [-1, typemax(Int)]
1102-
for dim in bad_dims, N in [1, 2, 3, 4, 10], T in Any[Int, Union{Missing,Nothing}, Nothing, Any]
1103+
@testset "$dim, $N, $T" for dim in bad_dims, N in Ns, T in Ts
11031104
dims = ntuple(i->dim, N)
11041105
@test @eval !fully_eliminated() do
11051106
Array{$T,$N}(undef, $(dims...))

base/boot.jl

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -557,12 +557,7 @@ struct UndefInitializer end
557557
const undef = UndefInitializer()
558558

559559
# type and dimensionality specified
560-
(self::Type{GenericMemory{kind,T,addrspace}})(::UndefInitializer, m::Int) where {T,addrspace,kind} =
561-
if isdefined(self, :instance) && m === 0
562-
self.instance
563-
else
564-
ccall(:jl_alloc_genericmemory, Ref{GenericMemory{kind,T,addrspace}}, (Any, Int), self, m)
565-
end
560+
(self::Type{GenericMemory{kind,T,addrspace}})(::UndefInitializer, m::Int) where {T,addrspace,kind} = Core.memorynew(self, m)
566561
(self::Type{GenericMemory{kind,T,addrspace}})(::UndefInitializer, d::NTuple{1,Int}) where {T,kind,addrspace} = self(undef, getfield(d,1))
567562
# empty vector constructor
568563
(self::Type{GenericMemory{kind,T,addrspace}})() where {T,kind,addrspace} = self(undef, 0)

base/essentials.jl

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,17 @@ default_access_order(a::GenericMemory{:atomic}) = :monotonic
382382
default_access_order(a::GenericMemoryRef{:not_atomic}) = :not_atomic
383383
default_access_order(a::GenericMemoryRef{:atomic}) = :monotonic
384384

385-
getindex(A::GenericMemory, i::Int) = (@_noub_if_noinbounds_meta;
386-
memoryrefget(memoryrefnew(memoryrefnew(A), i, @_boundscheck), default_access_order(A), false))
385+
# bootstrap version for Memory{Any}
386+
#getindex(A::Memory{Any}, i::Int) = (@_noub_if_noinbounds_meta;
387+
# memoryrefget(memoryrefnew(memoryrefnew(A), i, @_boundscheck), default_access_order(A), false))
388+
389+
function getindex(A::GenericMemory, i::Int)
390+
@_noub_if_noinbounds_meta
391+
if @_boundscheck
392+
ult_int(bitcast(UInt, sub_int(i, 1)), bitcast(UInt, A.length)) || throw_boundserror(A, (i,))
393+
end
394+
memoryrefget(memoryrefnew(memoryrefnew(A), i, false), default_access_order(A), false)
395+
end
387396
getindex(A::GenericMemoryRef) = memoryrefget(A, default_access_order(A), @_boundscheck)
388397

389398
"""

base/genericmemory.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,10 @@ getindex(A::Memory, c::Colon) = copy(A)
241241

242242
## Indexing: setindex! ##
243243

244-
function _setindex!(A::Memory{T}, x::T, i1::Int) where {T}
245-
ref = memoryrefnew(memoryref(A), i1, @_boundscheck)
246-
memoryrefset!(ref, x, :not_atomic, @_boundscheck)
244+
function _setindex!(A::Memory{T}, x::T, i::Int) where {T}
245+
@boundscheck Core.Intrinsics.ult_int(i, A.length)
246+
ref = memoryrefnew(memoryref(A), i, false)
247+
memoryrefset!(ref, x, :not_atomic, false)
247248
return A
248249
end
249250

doc/src/manual/performance-tips.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,12 +1058,12 @@ the output. As a trivial example, compare
10581058

10591059
```jldoctest prealloc
10601060
julia> function xinc(x)
1061-
return [x, x+1, x+2]
1061+
return [x + i for i in 1:3000]
10621062
end;
10631063
10641064
julia> function loopinc()
10651065
y = 0
1066-
for i = 1:10^7
1066+
for i = 1:10^5
10671067
ret = xinc(i)
10681068
y += ret[2]
10691069
end
@@ -1075,16 +1075,16 @@ with
10751075

10761076
```jldoctest prealloc
10771077
julia> function xinc!(ret::AbstractVector{T}, x::T) where T
1078-
ret[1] = x
1079-
ret[2] = x+1
1080-
ret[3] = x+2
1078+
for i in 1:3000
1079+
ret[i] = x+i
1080+
end
10811081
nothing
10821082
end;
10831083
10841084
julia> function loopinc_prealloc()
1085-
ret = Vector{Int}(undef, 3)
1085+
ret = Vector{Int}(undef, 3000)
10861086
y = 0
1087-
for i = 1:10^7
1087+
for i = 1:10^5
10881088
xinc!(ret, i)
10891089
y += ret[2]
10901090
end
@@ -1096,12 +1096,12 @@ Timing results:
10961096

10971097
```jldoctest prealloc; filter = r"[0-9\.]+ seconds \(.*?\)"
10981098
julia> @time loopinc()
1099-
0.529894 seconds (40.00 M allocations: 1.490 GiB, 12.14% gc time)
1100-
50000015000000
1099+
0.297454 seconds (200.00 k allocations: 2.239 GiB, 39.80% gc time)
1100+
5000250000
11011101
11021102
julia> @time loopinc_prealloc()
1103-
0.030850 seconds (6 allocations: 288 bytes)
1104-
50000015000000
1103+
0.009410 seconds (2 allocations: 23.477 KiB)
1104+
5000250000
11051105
```
11061106

11071107
Preallocation has other advantages, for example by allowing the caller to control the "output"

src/array.c

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,6 @@
1616
extern "C" {
1717
#endif
1818

19-
#if defined(_P64) && defined(UINT128MAX)
20-
typedef __uint128_t wideint_t;
21-
#else
22-
typedef uint64_t wideint_t;
23-
#endif
24-
2519
#define MAXINTVAL (((size_t)-1)>>1)
2620

2721
JL_DLLEXPORT int jl_array_validate_dims(size_t *nel, uint32_t ndims, size_t *dims)
@@ -30,10 +24,9 @@ JL_DLLEXPORT int jl_array_validate_dims(size_t *nel, uint32_t ndims, size_t *dim
3024
size_t _nel = 1;
3125
for (i = 0; i < ndims; i++) {
3226
size_t di = dims[i];
33-
wideint_t prod = (wideint_t)_nel * (wideint_t)di;
34-
if (prod >= (wideint_t) MAXINTVAL || di >= MAXINTVAL)
27+
int overflow = __builtin_mul_overflow(_nel, di, &_nel);
28+
if (overflow || di >= MAXINTVAL)
3529
return 1;
36-
_nel = prod;
3730
}
3831
*nel = _nel;
3932
return 0;
@@ -204,7 +197,7 @@ JL_DLLEXPORT void jl_array_grow_end(jl_array_t *a, size_t inc)
204197
int isbitsunion = jl_genericmemory_isbitsunion(a->ref.mem);
205198
size_t newnrows = n + inc;
206199
if (!isbitsunion && elsz == 0) {
207-
jl_genericmemory_t *newmem = jl_alloc_genericmemory(mtype, MAXINTVAL - 1);
200+
jl_genericmemory_t *newmem = jl_alloc_genericmemory(mtype, MAXINTVAL - 2);
208201
a->ref.mem = newmem;
209202
jl_gc_wb(a, newmem);
210203
a->dimsize[0] = newnrows;

src/builtin_proto.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ DECLARE_BUILTIN(is);
4646
DECLARE_BUILTIN(isa);
4747
DECLARE_BUILTIN(isdefined);
4848
DECLARE_BUILTIN(issubtype);
49+
DECLARE_BUILTIN(memorynew);
4950
DECLARE_BUILTIN(memoryref);
5051
DECLARE_BUILTIN(memoryref_isassigned);
5152
DECLARE_BUILTIN(memoryrefget);

src/builtins.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1675,6 +1675,15 @@ JL_CALLABLE(jl_f__typevar)
16751675
}
16761676

16771677
// genericmemory ---------------------------------------------------------------------
1678+
JL_CALLABLE(jl_f_memorynew)
1679+
{
1680+
JL_NARGS(memorynew, 2, 2);
1681+
jl_datatype_t *jl_genericmemory_type_type = jl_datatype_type;
1682+
JL_TYPECHK(memorynew, genericmemory_type, args[0]);
1683+
JL_TYPECHK(memorynew, long, args[1]);
1684+
size_t nel = jl_unbox_long(args[1]);
1685+
return (jl_value_t*)jl_alloc_genericmemory(args[0], nel);
1686+
}
16781687

16791688
JL_CALLABLE(jl_f_memoryref)
16801689
{
@@ -2441,6 +2450,7 @@ void jl_init_primitives(void) JL_GC_DISABLED
24412450
jl_builtin_setglobalonce = add_builtin_func("setglobalonce!", jl_f_setglobalonce);
24422451

24432452
// memory primitives
2453+
jl_builtin_memorynew = add_builtin_func("memorynew", jl_f_memorynew);
24442454
jl_builtin_memoryref = add_builtin_func("memoryrefnew", jl_f_memoryref);
24452455
jl_builtin_memoryrefoffset = add_builtin_func("memoryrefoffset", jl_f_memoryrefoffset);
24462456
jl_builtin_memoryrefget = add_builtin_func("memoryrefget", jl_f_memoryrefget);

src/ccall.cpp

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1877,33 +1877,6 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs)
18771877
JL_GC_POP();
18781878
return mark_julia_type(ctx, obj, true, jl_any_type);
18791879
}
1880-
else if (is_libjulia_func(jl_alloc_genericmemory)) {
1881-
++CCALL_STAT(jl_alloc_genericmemory);
1882-
assert(lrt == ctx.types().T_prjlvalue);
1883-
assert(!isVa && !llvmcall && nccallargs == 2);
1884-
const jl_cgval_t &typ = argv[0];
1885-
const jl_cgval_t &nel = argv[1];
1886-
auto arg_typename = [&] JL_NOTSAFEPOINT {
1887-
auto istyp = argv[0].constant;
1888-
std::string type_str;
1889-
if (istyp && jl_is_datatype(istyp) && jl_is_genericmemory_type(istyp)){
1890-
auto eltype = jl_tparam1(istyp);
1891-
if (jl_is_datatype(eltype))
1892-
type_str = jl_symbol_name(((jl_datatype_t*)eltype)->name->name);
1893-
else if (jl_is_uniontype(eltype))
1894-
type_str = "Union";
1895-
else
1896-
type_str = "<unknown type>";
1897-
}
1898-
else
1899-
type_str = "<unknown type>";
1900-
return "Memory{" + type_str + "}[]";
1901-
};
1902-
auto alloc = ctx.builder.CreateCall(prepare_call(jl_allocgenericmemory), { boxed(ctx,typ), emit_unbox(ctx, ctx.types().T_size, nel, (jl_value_t*)jl_ulong_type)});
1903-
setName(ctx.emission_context, alloc, arg_typename);
1904-
JL_GC_POP();
1905-
return mark_julia_type(ctx, alloc, true, jl_any_type);
1906-
}
19071880
else if (is_libjulia_func(memcpy) && (rt == (jl_value_t*)jl_nothing_type || jl_is_cpointer_type(rt))) {
19081881
++CCALL_STAT(memcpy);
19091882
const jl_cgval_t &dst = argv[0];

0 commit comments

Comments
 (0)