Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions base/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ include("options.jl")
include("promotion.jl")
include("tuple.jl")
include("expr.jl")
Pair{A, B}(@nospecialize(a), @nospecialize(b)) where {A, B} = (@_inline_meta; Pair{A, B}(convert(A, a)::A, convert(B, b)::B))
#Pair{Any, B}(@nospecialize(a::Any), b) where {B} = (@_inline_meta; Pair{Any, B}(a, Base.convert(B, b)::B))
#Pair{A, Any}(a, @nospecialize(b::Any)) where {A} = (@_inline_meta; Pair{A, Any}(Base.convert(A, a)::A, b))
include("pair.jl")
include("traits.jl")
include("range.jl")
Expand Down
14 changes: 13 additions & 1 deletion base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ export
# key types
Any, DataType, Vararg, NTuple,
Tuple, Type, UnionAll, TypeVar, Union, Nothing, Cvoid,
AbstractArray, DenseArray, NamedTuple,
AbstractArray, DenseArray, NamedTuple, Pair,
# special objects
Function, Method,
Module, Symbol, Task, Array, UndefInitializer, undef, WeakRef, VecElement,
Expand Down Expand Up @@ -813,4 +813,16 @@ _parse = nothing
# support for deprecated uses of internal _apply function
_apply(x...) = Core._apply_iterate(Main.Base.iterate, x...)

struct Pair{A, B}
first::A
second::B
# if we didn't inline this, it's probably because the callsite was actually dynamic
# to avoid potentially compiling many copies of this, we mark the arguments with `@nospecialize`
# but also mark the whole function with `@inline` to ensure we will inline it whenever possible
# (even if `convert(::Type{A}, a::A)` for some reason was expensive)
Comment on lines +819 to +822
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this comment have been moved to Base.jl? Or which method does it belong to?

Pair(a, b) = new{typeof(a), typeof(b)}(a, b)
Pair{A, B}(a::A, b::B) where {A, B} = new(a, b)
Pair{Any, Any}(@nospecialize(a::Any), @nospecialize(b::Any)) = new(a, b)
end

ccall(:jl_set_istopmod, Cvoid, (Any, Bool), Core, true)
64 changes: 50 additions & 14 deletions base/compiler/tfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -466,29 +466,51 @@ add_tfunc(Core._typevar, 3, 3, typevar_tfunc, 100)
add_tfunc(applicable, 1, INT_INF, (@nospecialize(f), args...)->Bool, 100)
add_tfunc(Core.Intrinsics.arraylen, 1, 1, @nospecialize(x)->Int, 4)
add_tfunc(arraysize, 2, 2, (@nospecialize(a), @nospecialize(d))->Int, 4)

function pointer_eltype(@nospecialize(ptr))
a = widenconst(ptr)
if a <: Ptr
if isa(a, DataType) && isa(a.parameters[1], Type)
return a.parameters[1]
elseif isa(a, UnionAll) && !has_free_typevars(a)
unw = unwrap_unionall(a)
if isa(unw, DataType)
return rewrap_unionall(unw.parameters[1], a)
end
if !has_free_typevars(a)
unw = unwrap_unionall(a)
if isa(unw, DataType) && unw.name === Ptr.body.name
T = unw.parameters[1]
T isa Type && return rewrap_unionall(T, a)
end
end
return Any
end
function atomic_pointermodify_tfunc(ptr, op, v, order)
@nospecialize
a = widenconst(ptr)
if !has_free_typevars(a)
unw = unwrap_unionall(a)
if isa(unw, DataType) && unw.name === Ptr.body.name
T = unw.parameters[1]
# note: we could sometimes refine this to a PartialStruct if we analyzed `op(T, T)::T`
T isa Type && return rewrap_unionall(Pair{T, T}, a)
end
end
return Pair
end
function atomic_pointerreplace_tfunc(ptr, x, v, success_order, failure_order)
@nospecialize
a = widenconst(ptr)
if !has_free_typevars(a)
unw = unwrap_unionall(a)
if isa(unw, DataType) && unw.name === Ptr.body.name
T = unw.parameters[1]
T isa Type && return rewrap_unionall(ccall(:jl_apply_cmpswap_type, Any, (Any,), T), a)
end
end
return ccall(:jl_apply_cmpswap_type, Any, (Any,), T) where T
end
add_tfunc(pointerref, 3, 3, (a, i, align) -> (@nospecialize; pointer_eltype(a)), 4)
add_tfunc(pointerset, 4, 4, (a, v, i, align) -> (@nospecialize; a), 5)

add_tfunc(atomic_fence, 1, 1, (order) -> (@nospecialize; Nothing), 4)
add_tfunc(atomic_pointerref, 2, 2, (a, order) -> (@nospecialize; pointer_eltype(a)), 4)
add_tfunc(atomic_pointerset, 3, 3, (a, v, order) -> (@nospecialize; a), 5)
add_tfunc(atomic_pointerswap, 3, 3, (a, v, order) -> (@nospecialize; pointer_eltype(a)), 5)
add_tfunc(atomic_pointermodify, 4, 4, (a, op, v, order) -> (@nospecialize; T = pointer_eltype(a); Tuple{T, T}), 5)
add_tfunc(atomic_pointerreplace, 5, 5, (a, x, v, success_order, failure_order) -> (@nospecialize; Tuple{pointer_eltype(a), Bool}), 5)
add_tfunc(atomic_pointermodify, 4, 4, atomic_pointermodify_tfunc, 5)
add_tfunc(atomic_pointerreplace, 5, 5, atomic_pointerreplace_tfunc, 5)

# more accurate typeof_tfunc for vararg tuples abstract only in length
function typeof_concrete_vararg(t::DataType)
Expand Down Expand Up @@ -911,11 +933,25 @@ setfield!_tfunc(o, f, v) = (@nospecialize; v)

swapfield!_tfunc(o, f, v, order) = (@nospecialize; getfield_tfunc(o, f))
swapfield!_tfunc(o, f, v) = (@nospecialize; getfield_tfunc(o, f))
modifyfield!_tfunc(o, f, op, v, order) = (@nospecialize; T = getfield_tfunc(o, f); T === Bottom ? T : Tuple{T, T})
modifyfield!_tfunc(o, f, op, v) = (@nospecialize; T = getfield_tfunc(o, f); T === Bottom ? T : Tuple{T, T}) # TODO: also model op(o.f, v) call
modifyfield!_tfunc(o, f, op, v, order) = (@nospecialize; modifyfield!_tfunc(o, f, op, v))
function modifyfield!_tfunc(o, f, op, v)
@nospecialize
T = _fieldtype_tfunc(o, isconcretetype(o), f)
T === Bottom && return Bottom
# note: we could sometimes refine this to a PartialStruct if we analyzed `op(o.f, v)::T`
PT = Const(Pair)
return instanceof_tfunc(apply_type_tfunc(PT, T, T))[1]
end
replacefield!_tfunc(o, f, x, v, success_order, failure_order) = (@nospecialize; replacefield!_tfunc(o, f, x, v))
replacefield!_tfunc(o, f, x, v, success_order) = (@nospecialize; replacefield!_tfunc(o, f, x, v))
replacefield!_tfunc(o, f, x, v) = (@nospecialize; T = getfield_tfunc(o, f); T === Bottom ? T : Tuple{widenconst(T), Bool})
function replacefield!_tfunc(o, f, x, v)
@nospecialize
T = _fieldtype_tfunc(o, isconcretetype(o), f)
T === Bottom && return Bottom
PT = Const(ccall(:jl_apply_cmpswap_type, Any, (Any,), T) where T)
return instanceof_tfunc(apply_type_tfunc(PT, T))[1]
end

# we could use tuple_tfunc instead of widenconst, but `o` is mutable, so that is unlikely to be beneficial

add_tfunc(getfield, 2, 4, getfield_tfunc, 1)
Expand Down
12 changes: 6 additions & 6 deletions base/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -523,16 +523,16 @@ julia> @atomic a.x += 1 # increment field x of a, with sequential consistency
3

julia> @atomic a.x + 1 # increment field x of a, with sequential consistency
(3, 4)
3 => 4

julia> @atomic a.x # fetch field x of a, with sequential consistency
4

julia> @atomic max(a.x, 10) # change field x of a to the max value, with sequential consistency
(4, 10)
4 => 10

julia> @atomic a.x max 5 # again change field x of a to the max value, with sequential consistency
(10, 10)
10 => 10
```
"""
macro atomic(ex)
Expand Down Expand Up @@ -653,18 +653,18 @@ julia> a = Atomic(1)
Atomic{Int64}(1)

julia> @atomicreplace a.x 1 => 2 # replace field x of a with 2 if it was 1, with sequential consistency
(1, true)
(old = 1, success = true)

julia> @atomic a.x # fetch field x of a, with sequential consistency
2

julia> @atomicreplace a.x 1 => 2 # replace field x of a with 2 if it was 1, with sequential consistency
(2, false)
(old = 2, success = false)

julia> xchg = 2 => 0; # replace field x of a with 0 if it was 1, with sequential consistency

julia> @atomicreplace a.x xchg
(2, true)
(old = 2, success = true)

julia> @atomic a.x # fetch field x of a, with sequential consistency
0
Expand Down
13 changes: 0 additions & 13 deletions base/pair.jl
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

struct Pair{A, B}
first::A
second::B
function Pair{A, B}(@nospecialize(a), @nospecialize(b)) where {A, B}
@_inline_meta
# if we didn't inline this, it's probably because the callsite was actually dynamic
# to avoid potentially compiling many copies of this, we mark the arguments with `@nospecialize`
# but also mark the whole function with `@inline` to ensure we will inline it whenever possible
# (even if `convert(::Type{A}, a::A)` for some reason was expensive)
return new(a, b)
end
end
Pair(a, b) = Pair{typeof(a), typeof(b)}(a, b)
const => = Pair

"""
Expand Down
17 changes: 8 additions & 9 deletions src/cgutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1560,9 +1560,8 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx,
Value *Success = emit_f_is(ctx, cmp, ghostValue(jltype));
Success = ctx.builder.CreateZExt(Success, T_int8);
jl_cgval_t argv[2] = {ghostValue(jltype), mark_julia_type(ctx, Success, false, jl_bool_type)};
// TODO: do better here
Value *instr = emit_jlcall(ctx, jltuple_func, V_rnull, argv, 2, JLCALL_F_CC);
return mark_julia_type(ctx, instr, true, jl_any_type);
jl_datatype_t *rettyp = jl_apply_cmpswap_type(jltype);
return emit_new_struct(ctx, (jl_value_t*)rettyp, 2, argv);
}
else {
return ghostValue(jltype);
Expand Down Expand Up @@ -1803,10 +1802,10 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx,
}
oldval = mark_julia_type(ctx, instr, isboxed, jltype);
if (isreplacefield) {
// TODO: do better here
Success = ctx.builder.CreateZExt(Success, T_int8);
jl_cgval_t argv[2] = {oldval, mark_julia_type(ctx, Success, false, jl_bool_type)};
instr = emit_jlcall(ctx, jltuple_func, V_rnull, argv, 2, JLCALL_F_CC);
oldval = mark_julia_type(ctx, instr, true, jl_any_type);
jl_datatype_t *rettyp = jl_apply_cmpswap_type(jltype);
oldval = emit_new_struct(ctx, (jl_value_t*)rettyp, 2, argv);
}
}
return oldval;
Expand Down Expand Up @@ -3247,10 +3246,10 @@ static jl_cgval_t emit_setfield(jl_codectx_t &ctx,
if (needlock)
emit_lockstate_value(ctx, strct, false);
if (isreplacefield) {
Success = ctx.builder.CreateZExt(Success, T_int8);
jl_cgval_t argv[2] = {oldval, mark_julia_type(ctx, Success, false, jl_bool_type)};
// TODO: do better here
Value *instr = emit_jlcall(ctx, jltuple_func, V_rnull, argv, 2, JLCALL_F_CC);
oldval = mark_julia_type(ctx, instr, true, jl_any_type);
jl_datatype_t *rettyp = jl_apply_cmpswap_type(jfty);
oldval = emit_new_struct(ctx, (jl_value_t*)rettyp, 2, argv);
}
return oldval;
}
Expand Down
1 change: 1 addition & 0 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,7 @@ static CallInst *emit_jlcall(jl_codectx_t &ctx, JuliaFunction *theFptr, Value *t
jl_cgval_t *args, size_t nargs, CallingConv::ID cc);
static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2,
Value *nullcheck1 = nullptr, Value *nullcheck2 = nullptr);
static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t nargs, const jl_cgval_t *argv);

static Value *literal_pointer_val(jl_codectx_t &ctx, jl_value_t *p);
static GlobalVariable *prepare_global_in(Module *M, GlobalVariable *G);
Expand Down
47 changes: 19 additions & 28 deletions src/datatype.c
Original file line number Diff line number Diff line change
Expand Up @@ -953,18 +953,13 @@ JL_DLLEXPORT int jl_atomic_bool_cmpswap_bits(char *dst, const jl_value_t *expect
return success;
}

JL_DLLEXPORT jl_value_t *jl_atomic_cmpswap_bits(jl_datatype_t *dt, char *dst, const jl_value_t *expected, const jl_value_t *src, int nb)
JL_DLLEXPORT jl_value_t *jl_atomic_cmpswap_bits(jl_datatype_t *dt, jl_datatype_t *rettyp, char *dst, const jl_value_t *expected, const jl_value_t *src, int nb)
{
// dst must have the required alignment for an atomic of the given size
// n.b.: this does not spuriously fail if there are padding bits
jl_value_t *params[2];
params[0] = (jl_value_t*)dt;
params[1] = (jl_value_t*)jl_bool_type;
jl_datatype_t *tuptyp = jl_apply_tuple_type_v(params, 2);
JL_GC_PROMISE_ROOTED(tuptyp); // (JL_ALWAYS_LEAFTYPE)
int isptr = jl_field_isptr(tuptyp, 0);
jl_task_t *ct = jl_current_task;
jl_value_t *y = jl_gc_alloc(ct->ptls, isptr ? nb : tuptyp->size, isptr ? dt : tuptyp);
int isptr = jl_field_isptr(rettyp, 0);
jl_value_t *y = jl_gc_alloc(ct->ptls, isptr ? nb : rettyp->size, isptr ? dt : rettyp);
int success;
jl_datatype_t *et = (jl_datatype_t*)jl_typeof(expected);
if (nb == 0) {
Expand Down Expand Up @@ -1053,7 +1048,7 @@ JL_DLLEXPORT jl_value_t *jl_atomic_cmpswap_bits(jl_datatype_t *dt, char *dst, co
}
if (isptr) {
JL_GC_PUSH1(&y);
jl_value_t *z = jl_gc_alloc(ct->ptls, tuptyp->size, tuptyp);
jl_value_t *z = jl_gc_alloc(ct->ptls, rettyp->size, rettyp);
*(jl_value_t**)z = y;
JL_GC_POP();
y = z;
Expand Down Expand Up @@ -1658,8 +1653,11 @@ jl_value_t *modify_nth_field(jl_datatype_t *st, jl_value_t *v, size_t i, jl_valu
args[0] = r;
jl_gc_safepoint();
}
// args[0] == r (old); args[1] == y (new)
args[0] = jl_f_tuple(NULL, args, 2);
// args[0] == r (old)
// args[1] == y (new)
jl_datatype_t *rettyp = jl_apply_modify_type(ty);
JL_GC_PROMISE_ROOTED(rettyp); // (JL_ALWAYS_LEAFTYPE)
args[0] = jl_new_struct(rettyp, args[0], args[1]);
JL_GC_POP();
return args[0];
}
Expand All @@ -1671,6 +1669,8 @@ jl_value_t *replace_nth_field(jl_datatype_t *st, jl_value_t *v, size_t i, jl_val
jl_type_error("replacefield!", ty, rhs);
size_t offs = jl_field_offset(st, i);
jl_value_t *r = expected;
jl_datatype_t *rettyp = jl_apply_cmpswap_type(ty);
JL_GC_PROMISE_ROOTED(rettyp); // (JL_ALWAYS_LEAFTYPE)
if (jl_field_isptr(st, i)) {
jl_value_t **p = (jl_value_t**)((char*)v + offs);
int success;
Expand All @@ -1683,19 +1683,16 @@ jl_value_t *replace_nth_field(jl_datatype_t *st, jl_value_t *v, size_t i, jl_val
if (success || !jl_egal(r, expected))
break;
}
jl_value_t **args;
JL_GC_PUSHARGS(args, 2);
args[0] = r;
args[1] = success ? jl_true : jl_false;
r = jl_f_tuple(NULL, args, 2);
JL_GC_PUSH1(&r);
r = jl_new_struct(rettyp, r, success ? jl_true : jl_false);
JL_GC_POP();
}
else {
int hasptr;
int isunion = jl_is_uniontype(ty);
int needlock;
jl_value_t *rty = ty;
size_t fsz;
size_t fsz = jl_field_size(st, i);
if (isunion) {
assert(!isatomic);
hasptr = 0;
Expand All @@ -1708,7 +1705,7 @@ jl_value_t *replace_nth_field(jl_datatype_t *st, jl_value_t *v, size_t i, jl_val
needlock = (isatomic && fsz > MAX_ATOMIC_SIZE);
}
if (isatomic && !needlock) {
r = jl_atomic_cmpswap_bits((jl_datatype_t*)rty, (char*)v + offs, r, rhs, fsz);
r = jl_atomic_cmpswap_bits((jl_datatype_t*)ty, rettyp, (char*)v + offs, r, rhs, fsz);
int success = *((uint8_t*)r + fsz);
if (success && hasptr)
jl_gc_multi_wb(v, rhs); // rhs is immutable
Expand All @@ -1717,23 +1714,17 @@ jl_value_t *replace_nth_field(jl_datatype_t *st, jl_value_t *v, size_t i, jl_val
jl_task_t *ct = jl_current_task;
uint8_t *psel;
if (isunion) {
size_t fsz = jl_field_size(st, i);
psel = &((uint8_t*)v)[offs + fsz - 1];
rty = jl_nth_union_component(rty, *psel);
}
jl_value_t *params[2];
params[0] = rty;
params[1] = (jl_value_t*)jl_bool_type;
jl_datatype_t *tuptyp = jl_apply_tuple_type_v(params, 2);
JL_GC_PROMISE_ROOTED(tuptyp); // (JL_ALWAYS_LEAFTYPE)
assert(!jl_field_isptr(tuptyp, 0));
r = jl_gc_alloc(ct->ptls, tuptyp->size, (jl_value_t*)tuptyp);
assert(!jl_field_isptr(rettyp, 0));
r = jl_gc_alloc(ct->ptls, rettyp->size, (jl_value_t*)rettyp);
int success = (rty == jl_typeof(expected));
if (needlock)
jl_lock_value(v);
size_t fsz = jl_datatype_size((jl_datatype_t*)rty); // need to shrink-wrap the final copy
memcpy((char*)r, (char*)v + offs, fsz);
memcpy((char*)r, (char*)v + offs, fsz); // copy field, including union bits
if (success) {
size_t fsz = jl_datatype_size((jl_datatype_t*)rty); // need to shrink-wrap the final copy
if (((jl_datatype_t*)rty)->layout->haspadding)
success = jl_egal__bits(r, expected, (jl_datatype_t*)rty);
else
Expand Down
3 changes: 3 additions & 0 deletions src/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2780,6 +2780,7 @@ static void jl_gc_queue_thread_local(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp
}

void jl_gc_mark_enqueued_tasks(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp);
extern jl_value_t *cmpswap_names JL_GLOBALLY_ROOTED;

// mark the initial root set
static void mark_roots(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp)
Expand Down Expand Up @@ -2811,6 +2812,8 @@ static void mark_roots(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp)

// constants
gc_mark_queue_obj(gc_cache, sp, jl_emptytuple_type);
if (cmpswap_names != NULL)
gc_mark_queue_obj(gc_cache, sp, cmpswap_names);
}

// find unmarked objects that need to be finalized from the finalizer list "list".
Expand Down
1 change: 1 addition & 0 deletions src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,7 @@ static void post_boot_hooks(void)
jl_methoderror_type = (jl_datatype_t*)core("MethodError");
jl_loaderror_type = (jl_datatype_t*)core("LoadError");
jl_initerror_type = (jl_datatype_t*)core("InitError");
jl_pair_type = core("Pair");

jl_weakref_type = (jl_datatype_t*)core("WeakRef");
jl_vecelement_typename = ((jl_datatype_t*)jl_unwrap_unionall(core("VecElement")))->name;
Expand Down
1 change: 1 addition & 0 deletions src/jl_exported_data.inc
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
XX(jl_number_type) \
XX(jl_opaque_closure_type) \
XX(jl_opaque_closure_typename) \
XX(jl_pair_type) \
XX(jl_partial_opaque_type) \
XX(jl_partial_struct_type) \
XX(jl_phicnode_type) \
Expand Down
2 changes: 2 additions & 0 deletions src/jl_exported_funcs.inc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
XX(jl_alloc_svec_uninit) \
XX(jl_alloc_vec_any) \
XX(jl_apply_array_type) \
XX(jl_apply_cmpswap_type) \
XX(jl_apply_generic) \
XX(jl_apply_tuple_type) \
XX(jl_apply_tuple_type_v) \
Expand Down Expand Up @@ -549,3 +550,4 @@
XX(jl_vprintf) \
XX(jl_wakeup_thread) \
XX(jl_yield) \

Loading