Skip to content

WIP/RFC unbox more immutables #18632

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 25 commits into from
Closed
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
30 changes: 21 additions & 9 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ end

asize_from(a::Array, n) = n > ndims(a) ? () : (arraysize(a,n), asize_from(a, n+1)...)

is_stored_unboxed(T) = isleaftype(T) && !T.mutable

length(a::Array) = arraylen(a)
elsize{T}(a::Array{T}) = isbits(T) ? sizeof(T) : sizeof(Ptr)
elsize{T}(a::Array{T}) = is_stored_unboxed(T) ? sizeof(T) : sizeof(Ptr)
sizeof(a::Array) = elsize(a) * length(a)

function isassigned{T}(a::Array{T}, i::Int...)
Expand All @@ -50,8 +52,15 @@ function unsafe_copy!{T}(dest::Array{T}, doffs, src::Array{T}, soffs, n)
if isbits(T)
unsafe_copy!(pointer(dest, doffs), pointer(src, soffs), n)
else
ccall(:jl_array_ptr_copy, Void, (Any, Ptr{Void}, Any, Ptr{Void}, Int),
dest, pointer(dest, doffs), src, pointer(src, soffs), n)
# TODO check the proper ptrarray flag and write a fast path for the other case
if !is_stored_unboxed(T)
ccall(:jl_array_ptr_copy, Void, (Any, Ptr{Void}, Any, Ptr{Void}, Int),
dest, pointer(dest, doffs), src, pointer(src, soffs), n)
else
for i = 0:n-1
dest[i+doffs] = src[i+soffs]
end
end
end
return dest
end
Expand Down Expand Up @@ -928,22 +937,25 @@ function vcat{T}(arrays::Vector{T}...)
end
arr = Array{T,1}(n)
ptr = pointer(arr)
if isbits(T)
elsz = Core.sizeof(T)
else
elsz = Core.sizeof(Ptr{Void})
end
idx = 1
elsz = elsize(arr)
for a in arrays
na = length(a)
nba = na * elsz
if isbits(T)
ccall(:memcpy, Ptr{Void}, (Ptr{Void}, Ptr{Void}, UInt),
ptr, a, nba)
else
elseif !is_stored_unboxed(T)
ccall(:jl_array_ptr_copy, Void, (Any, Ptr{Void}, Any, Ptr{Void}, Int),
arr, ptr, a, pointer(a), na)
else
# TODO same as unsafe_copy!
for i = 1:na
arr[idx+i-1] = a[i]
end
end
ptr += nba
idx += na
end
return arr
end
Expand Down
9 changes: 7 additions & 2 deletions base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ if is(Int,Int64)
else
typealias UInt UInt32
end

abstract ValueType
abstract AbstractString

function Typeof end
Expand All @@ -209,7 +209,12 @@ immutable OutOfMemoryError <: Exception end
immutable ReadOnlyMemoryError<: Exception end
immutable SegmentationFault <: Exception end
immutable StackOverflowError <: Exception end
immutable UndefRefError <: Exception end
immutable UndefRefError <: Exception
a::Any
i::Any
UndefRefError() = new()
end
const empty_undefref_error = UndefRefError()
immutable UndefVarError <: Exception
var::Symbol
end
Expand Down
6 changes: 3 additions & 3 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1175,7 +1175,7 @@ function abstract_eval(e::ANY, vtypes::VarTable, sv::InferenceState)
t = abstract_eval_call(e, vtypes, sv)
elseif is(e.head,:null)
t = Void
elseif is(e.head,:new)
elseif is(e.head,:new) || is(e.head,:stack_new)
t = abstract_eval(e.args[1], vtypes, sv)
if isType(t)
t = t.parameters[1]
Expand Down Expand Up @@ -2316,7 +2316,7 @@ function effect_free(e::ANY, src::CodeInfo, mod::Module, allow_volatile::Bool)
else
return false
end
elseif head === :new
elseif head === :new || head === :stack_new
if !allow_volatile
a = ea[1]
typ = widenconst(exprtype(a, src, mod))
Expand Down Expand Up @@ -3597,7 +3597,7 @@ function is_allocation(e::ANY, sv::InferenceState)
isa(e, Expr) || return false
if is_known_call(e, tuple, sv.src, sv.mod)
return (length(e.args)-1,())
elseif e.head === :new
elseif e.head === :new || e.head === :stack_new
typ = widenconst(exprtype(e, sv.src, sv.mod))
if isleaftype(typ)
@assert(isa(typ,DataType))
Expand Down
1 change: 1 addition & 0 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ object_id(x::ANY) = ccall(:jl_object_id, UInt, (Any,), x)

immutable DataTypeLayout
nfields::UInt32
npointers::UInt32
alignment::UInt32
# alignment : 28;
# haspadding : 1;
Expand Down
13 changes: 12 additions & 1 deletion base/replutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,18 @@ function showerror(io::IO, ex::SystemError)
end
showerror(io::IO, ::DivideError) = print(io, "DivideError: integer division error")
showerror(io::IO, ::StackOverflowError) = print(io, "StackOverflowError:")
showerror(io::IO, ::UndefRefError) = print(io, "UndefRefError: access to undefined reference")
function showerror(io::IO, e::UndefRefError)
print(io, "UndefRefError: ")
if isdefined(e, :a)
if isa(e.a, Array)
print(io, "access to undefined element ", e.i, " of ", summary(e.a))
else
print(io, "access to undefined field `", fieldname(typeof(e.a), e.i), "` of ", e.a)
end
else
print(io, "access to undefined reference")
end
end
showerror(io::IO, ::EOFError) = print(io, "EOFError: read end of file")
showerror(io::IO, ex::ErrorException) = print(io, ex.msg)
showerror(io::IO, ex::KeyError) = print(io, "KeyError: key $(repr(ex.key)) not found")
Expand Down
99 changes: 86 additions & 13 deletions src/alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ JL_DLLEXPORT jl_value_t *jl_inexact_exception;
JL_DLLEXPORT jl_value_t *jl_undefref_exception;
jl_value_t *jl_interrupt_exception;
jl_datatype_t *jl_boundserror_type;
jl_datatype_t *jl_undefreferror_type;
jl_value_t *jl_memory_exception;
jl_value_t *jl_readonlymemory_exception;
union jl_typemap_t jl_cfunction_list;

jl_sym_t *call_sym; jl_sym_t *invoke_sym;
jl_sym_t *dots_sym; jl_sym_t *empty_sym;
jl_sym_t *module_sym; jl_sym_t *slot_sym;
Expand Down Expand Up @@ -103,6 +103,7 @@ jl_sym_t *inert_sym; jl_sym_t *vararg_sym;
jl_sym_t *unused_sym; jl_sym_t *static_parameter_sym;
jl_sym_t *polly_sym; jl_sym_t *inline_sym;
jl_sym_t *propagate_inbounds_sym;
jl_sym_t *stack_new_sym;

typedef struct {
int64_t a;
Expand Down Expand Up @@ -209,19 +210,27 @@ JL_DLLEXPORT void jl_set_nth_field(jl_value_t *v, size_t i, jl_value_t *rhs)
}
else {
jl_assign_bits((char*)v + offs, rhs);
jl_gc_multi_wb(v, rhs);
}
}

JL_DLLEXPORT int jl_field_isdefined(jl_value_t *v, size_t i)
{
jl_datatype_t *st = (jl_datatype_t*)jl_typeof(v);
size_t offs = jl_field_offset(st,i);
char *fld = (char*)v + offs;
if (jl_field_isptr(st,i)) {
return *(jl_value_t**)((char*)v + offs) != NULL;
return *(jl_value_t**)fld != NULL;
} else if (jl_field_hasptr(st, i)) {
jl_datatype_t *ft = (jl_datatype_t*)jl_field_type(st, i);
assert(ft->first_init_ptr >= 0);
char *first_ptr = fld + ft->first_init_ptr;
return *(jl_value_t**)first_ptr != NULL;
}
return 1;
}


JL_DLLEXPORT jl_value_t *jl_new_struct(jl_datatype_t *type, ...)
{
jl_ptls_t ptls = jl_get_ptls_states();
Expand Down Expand Up @@ -873,14 +882,18 @@ jl_datatype_t *jl_new_uninitialized_datatype(void)
t->hastypevars = 0;
t->haswildcard = 0;
t->isleaftype = 1;
t->boxed = 0;
t->first_init_ptr = -1;
t->layout = NULL;
return t;
}

static jl_datatype_layout_t *jl_get_layout(uint32_t nfields,
uint32_t alignment,
int haspadding,
jl_fielddesc32_t desc[])
jl_fielddesc32_t desc[],
uint32_t npointers,
uint32_t *pointers)
{
// compute the smallest fielddesc type that can hold the layout description
int fielddesc_type = 0;
Expand All @@ -891,9 +904,9 @@ static jl_datatype_layout_t *jl_get_layout(uint32_t nfields,
if (desc[i].size > max_size)
max_size = desc[i].size;
}
jl_fielddesc8_t maxdesc8 = { 0, max_size, max_offset };
jl_fielddesc16_t maxdesc16 = { 0, max_size, max_offset };
jl_fielddesc32_t maxdesc32 = { 0, max_size, max_offset };
jl_fielddesc8_t maxdesc8 = { 0, 0, max_size, max_offset };
jl_fielddesc16_t maxdesc16 = { 0, 0, max_size, max_offset };
jl_fielddesc32_t maxdesc32 = { 0, 0, max_size, max_offset };
if (maxdesc8.size != max_size || maxdesc8.offset != max_offset) {
fielddesc_type = 1;
if (maxdesc16.size != max_size || maxdesc16.offset != max_offset) {
Expand All @@ -908,8 +921,9 @@ static jl_datatype_layout_t *jl_get_layout(uint32_t nfields,
// allocate a new descriptor
uint32_t fielddesc_size = jl_fielddesc_size(fielddesc_type);
jl_datatype_layout_t *flddesc =
(jl_datatype_layout_t*)jl_gc_perm_alloc(sizeof(jl_datatype_layout_t) + nfields * fielddesc_size);
(jl_datatype_layout_t*)jl_gc_perm_alloc(sizeof(jl_datatype_layout_t) + nfields * fielddesc_size + npointers * sizeof(uint32_t));
flddesc->nfields = nfields;
flddesc->npointers = npointers;
flddesc->alignment = alignment;
flddesc->haspadding = haspadding;
flddesc->fielddesc_type = fielddesc_type;
Expand All @@ -924,21 +938,26 @@ static jl_datatype_layout_t *jl_get_layout(uint32_t nfields,
desc8[i].offset = desc[i].offset;
desc8[i].size = desc[i].size;
desc8[i].isptr = desc[i].isptr;
desc8[i].hasptr = desc[i].hasptr;
}
else if (fielddesc_type == 1) {
desc16[i].offset = desc[i].offset;
desc16[i].size = desc[i].size;
desc16[i].isptr = desc[i].isptr;
desc16[i].hasptr = desc[i].hasptr;
}
else {
desc32[i].offset = desc[i].offset;
desc32[i].size = desc[i].size;
desc32[i].isptr = desc[i].isptr;
desc32[i].hasptr = desc[i].hasptr;
}
if (desc[i].isptr)
if (desc[i].isptr || desc[i].hasptr)
ptrfree = 0;
}
flddesc->pointerfree = ptrfree;
uint32_t *desc_pointers = jl_dt_layout_pointers(flddesc);
memcpy(desc_pointers, pointers, sizeof(uint32_t)*npointers);
return flddesc;
}

Expand Down Expand Up @@ -1001,25 +1020,38 @@ void jl_compute_field_offsets(jl_datatype_t *st)
st == jl_simplevector_type ||
nfields != 0);

int32_t first_init_ptr = -1; // offset in bytes or -1
int ptrfree = 1;
uint32_t npointers = 0;
for (size_t i = 0; i < nfields; i++) {
jl_value_t *ty = jl_field_type(st, i);
size_t fsz, al;
if (jl_isbits(ty) && jl_is_leaf_type(ty) && ((jl_datatype_t*)ty)->layout) {
int32_t field_first_init_ptr = -1;
if (jl_is_unboxed(ty) && jl_is_leaf_type(ty) && ((jl_datatype_t*)ty)->layout) {
fsz = jl_datatype_size(ty);
// Should never happen
if (__unlikely(fsz > max_size))
jl_throw(jl_overflow_exception);
al = ((jl_datatype_t*)ty)->layout->alignment;
jl_datatype_layout_t *field_layout = ((jl_datatype_t*)ty)->layout;
al = field_layout->alignment;
desc[i].isptr = 0;
desc[i].hasptr = !field_layout->pointerfree;
if (((jl_datatype_t*)ty)->layout->haspadding)
haspadding = 1;
npointers += field_layout->npointers;
if (((jl_datatype_t*)ty)->first_init_ptr >= 0)
field_first_init_ptr = ((jl_datatype_t*)ty)->first_init_ptr;
}
else {
fsz = sizeof(void*);
if (fsz > MAX_ALIGN)
fsz = MAX_ALIGN;
al = fsz;
desc[i].isptr = 1;
desc[i].hasptr = 0;
if (i < st->ninitialized)
field_first_init_ptr = 0;
npointers += 1;
}
if (al != 0) {
size_t alsz = LLT_ALIGN(sz, al);
Expand All @@ -1033,10 +1065,33 @@ void jl_compute_field_offsets(jl_datatype_t *st)
lastty = ty;
desc[i].offset = sz;
desc[i].size = fsz;
if (first_init_ptr < 0 && field_first_init_ptr >= 0)
first_init_ptr = sz + field_first_init_ptr;
if (__unlikely(max_offset - sz < fsz))
jl_throw(jl_overflow_exception);
if (desc[i].isptr | desc[i].hasptr)
ptrfree = 0;
sz += fsz;
}
st->first_init_ptr = first_init_ptr;

uint32_t *pointers = (uint32_t*)alloca(npointers*sizeof(uint32_t));
size_t ptr_i = 0;
for (size_t i = 0; i < nfields; i++) {
if (desc[i].isptr)
pointers[ptr_i++] = desc[i].offset;
else if(desc[i].hasptr) {
jl_datatype_layout_t *fl = ((jl_datatype_t*)jl_field_type(st, i))->layout;
uint32_t *f_ptrs = jl_dt_layout_pointers(fl);
for (size_t j = 0; j < fl->npointers; j++)
pointers[ptr_i++] = desc[i].offset + f_ptrs[j];
}
}

// if there are no pointer field that we know for sure are non null
// we can't represent #undef with this layout, so fall back to boxing
if (st->first_init_ptr < 0 && !ptrfree)
st->boxed = 1;
if (homogeneous && lastty!=NULL && jl_is_tuple_type(st)) {
// Some tuples become LLVM vectors with stronger alignment than what was calculated above.
unsigned al = jl_special_vector_alignment(nfields, lastty);
Expand All @@ -1047,7 +1102,7 @@ void jl_compute_field_offsets(jl_datatype_t *st)
st->size = LLT_ALIGN(sz, alignm);
if (st->size > sz)
haspadding = 1;
st->layout = jl_get_layout(nfields, alignm, haspadding, desc);
st->layout = jl_get_layout(nfields, alignm, haspadding, desc, npointers, pointers);
}

extern int jl_boot_file_loaded;
Expand All @@ -1057,6 +1112,14 @@ JL_DLLEXPORT jl_datatype_t *jl_new_datatype(jl_sym_t *name, jl_datatype_t *super
jl_svec_t *fnames, jl_svec_t *ftypes,
int abstract, int mutabl,
int ninitialized)
{
return jl_new_datatype_(name, super, parameters, fnames, ftypes, abstract, mutabl, ninitialized, 0);
}

jl_datatype_t* jl_new_datatype_(jl_sym_t* name, jl_datatype_t *super, jl_svec_t *parameters,
jl_svec_t* fnames, jl_svec_t *ftypes,
int abstract, int mutabl, int ninitialized,
int boxed)
{
jl_ptls_t ptls = jl_get_ptls_states();
jl_datatype_t *t=NULL;
Expand Down Expand Up @@ -1089,6 +1152,8 @@ JL_DLLEXPORT jl_datatype_t *jl_new_datatype(jl_sym_t *name, jl_datatype_t *super
t->abstract = abstract;
t->mutabl = mutabl;
t->ninitialized = ninitialized;
t->boxed = boxed;
t->first_init_ptr = -1;
t->instance = NULL;
t->struct_decl = NULL;
t->ditype = NULL;
Expand Down Expand Up @@ -1124,7 +1189,15 @@ JL_DLLEXPORT jl_datatype_t *jl_new_datatype(jl_sym_t *name, jl_datatype_t *super
else {
t->uid = jl_assign_type_uid();
if (t->types != NULL && t->isleaftype) {
static const jl_datatype_layout_t singleton_layout = {0, 1, 0, 1, 0};
static const jl_datatype_layout_t singleton_layout =
{
.nfields = 0,
.npointers = 0,
.alignment = 1,
.haspadding = 0,
.pointerfree = 1,
.fielddesc_type = 0
};
if (fnames == jl_emptysvec)
t->layout = &singleton_layout;
else
Expand All @@ -1145,7 +1218,7 @@ JL_DLLEXPORT jl_datatype_t *jl_new_bitstype(jl_value_t *name, jl_datatype_t *sup
if (alignm > MAX_ALIGN)
alignm = MAX_ALIGN;
bt->size = nbytes;
bt->layout = jl_get_layout(0, alignm, 0, NULL);
bt->layout = jl_get_layout(0, alignm, 0, NULL, 0, NULL);
return bt;
}

Expand Down
Loading