Skip to content
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

WIP: Allow constant fields in mutable types #11430

Closed
wants to merge 2 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
14 changes: 8 additions & 6 deletions base/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# notes: bits are stored in contiguous chunks
# unused bits must always be set to 0
type BitArray{N} <: DenseArray{Bool, N}
chunks::Vector{UInt64}
const chunks::Vector{UInt64}
len::Int
dims::NTuple{N,Int}
function BitArray(dims::Int...)
Expand All @@ -24,6 +24,12 @@ type BitArray{N} <: DenseArray{Bool, N}
N != 1 && (b.dims = dims)
return b
end

function BitArray(chunks::Vector{UInt64}, dims::NTuple{N,Int})
len = prod(dims)
num_bit_chunks(len) <= length(chunks) || throw(ArgumentError("chunks vector is too short for the given dimensions"))
return new(chunks, len, dims)
end
end

BitArray{N}(dims::NTuple{N,Int}) = BitArray{N}(dims...)
Expand Down Expand Up @@ -280,11 +286,7 @@ function reshape{N}(B::BitArray, dims::NTuple{N,Int})
prod(dims) == length(B) ||
throw(DimensionMismatch("new dimensions $(dims) must be consistent with array size $(length(B))"))
dims == size(B) && return B
Br = BitArray{N}(ntuple(N,i->0)...)
Br.chunks = B.chunks
Br.len = prod(dims)
N != 1 && (Br.dims = dims)
return Br
return BitArray{N}(B.chunks, dims)
end

## Conversions ##
Expand Down
8 changes: 1 addition & 7 deletions base/mmap.jl
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,5 @@ function mmap_bitarray{N}(dims::NTuple{N,Integer}, s::IOStream, offset::FileOffs
throw(ArgumentError("the given file does not contain a valid BitArray of size $(join(dims, 'x')) (open with \"r+\" mode to override)"))
end
end
B = BitArray{N}(ntuple(N,i->0)...)
B.chunks = chunks
B.len = n
if N != 1
B.dims = dims
end
return B
return BitArray{N}(chunks, dims)
end
5 changes: 3 additions & 2 deletions src/alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ jl_datatype_t *jl_new_uninitialized_datatype(size_t nfields)
return t;
}

void jl_compute_field_offsets(jl_datatype_t *st)
void jl_compute_field_offsets(jl_datatype_t *st, jl_svec_t *consts)
{
size_t sz = 0, alignm = 1;
int ptrfree = 1;
Expand Down Expand Up @@ -556,6 +556,7 @@ void jl_compute_field_offsets(jl_datatype_t *st)
jl_throw(jl_overflow_exception);
st->fields[i].offset = sz;
st->fields[i].size = fsz;
st->fields[i].isconst = (jl_svec_len(consts) == 0) ? 0 : jl_svecref(consts, i)==jl_true;
sz += fsz;
}
st->alignment = alignm;
Expand Down Expand Up @@ -628,7 +629,7 @@ 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)
jl_compute_field_offsets(t);
jl_compute_field_offsets(t, jl_emptysvec);
}
JL_GC_POP();
return t;
Expand Down
3 changes: 3 additions & 0 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,9 @@ JL_CALLABLE(jl_f_set_field)
if (!jl_subtype(args[2], ft, 1)) {
jl_type_error("setfield!", ft, args[2]);
}
if (st->fields[idx].isconst) {
jl_errorf("field in type %s is immutable", st->name->name->name);
}
jl_set_nth_field(v, idx, args[2]);
return args[2];
}
Expand Down
2 changes: 1 addition & 1 deletion src/cgutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1805,7 +1805,7 @@ static void emit_checked_write_barrier(jl_codectx_t *ctx, Value *parent, Value *
static Value *emit_setfield(jl_datatype_t *sty, Value *strct, size_t idx0,
Value *rhs, jl_codectx_t *ctx, bool checked, bool wb)
{
if (sty->mutabl || !checked) {
if ((sty->mutabl && !(sty->fields[idx0].isconst)) || !checked) {
Value *addr =
builder.CreateGEP(builder.CreateBitCast(strct, T_pint8),
ConstantInt::get(T_size, sty->fields[idx0].offset));
Expand Down
19 changes: 18 additions & 1 deletion src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1566,6 +1566,23 @@ static bool isbits_spec(jl_value_t *jt, bool allow_unsized = true)
(jl_is_datatype(jt) && jl_datatype_nfields(jt)>0)));
}

static bool is_constant_field(jl_datatype_t *ty, jl_value_t *fld)
{
if (!jl_is_leaf_type((jl_value_t*)ty))
return false;
jl_sym_t *name = NULL;
if (jl_is_quotenode(fld) && jl_is_symbol(jl_fieldref(fld,0))) {
name = (jl_sym_t*)jl_fieldref(fld,0);
}
for(size_t i=0; i < jl_svec_len(ty->types); i++) {
if (ty->fields[i].isconst &&
(name && name == jl_field_name(ty,i))) {
return true;
}
}
return false;
}

// does "ex" compute something that doesn't need a root over the whole function?
static bool is_stable_expr(jl_value_t *ex, jl_codectx_t *ctx)
{
Expand All @@ -1592,7 +1609,7 @@ static bool is_stable_expr(jl_value_t *ex, jl_codectx_t *ctx)
// something reached via getfield from a stable value is also stable.
if (jl_array_dim0(e->args) == 3) {
jl_value_t *ty = expr_type(jl_exprarg(e,1), ctx);
if ((fptr == &jl_f_get_field && jl_is_immutable_datatype(ty) &&
if ((fptr == &jl_f_get_field && (jl_is_immutable_datatype(ty) || is_constant_field((jl_datatype_t*)ty, jl_exprarg(e,2))) &&
is_getfield_nonallocating((jl_datatype_t*)ty, jl_exprarg(e,2)))) {
if (is_stable_expr(jl_exprarg(e,1), ctx))
return true;
Expand Down
4 changes: 2 additions & 2 deletions src/interpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl, size_t ng
temp = eval(args[2], locals, nl, ngensym); // field names
dt = jl_new_datatype((jl_sym_t*)name, jl_any_type, (jl_svec_t*)para,
(jl_svec_t*)temp, NULL,
0, args[5]==jl_true ? 1 : 0, jl_unbox_long(args[6]));
0, args[6]==jl_true ? 1 : 0, jl_unbox_long(args[7]));

jl_binding_t *b = jl_get_binding_wr(jl_current_module, (jl_sym_t*)name);
temp = b->value; // save old value
Expand All @@ -421,7 +421,7 @@ static jl_value_t *eval(jl_value_t *e, jl_value_t **locals, size_t nl, size_t ng
for(size_t i=0; i < jl_svec_len(para); i++) {
((jl_tvar_t*)jl_svecref(para,i))->bound = 0;
}
jl_compute_field_offsets(dt);
jl_compute_field_offsets(dt, (jl_svec_t*)eval(args[5], locals, nl, ngensym));
if (para == (jl_value_t*)jl_emptysvec && jl_is_datatype_singleton(dt)) {
dt->instance = newstruct(dt);
gc_wb(dt, dt->instance);
Expand Down
42 changes: 23 additions & 19 deletions src/jltypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2007,7 +2007,11 @@ static jl_value_t *inst_datatype(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **i
ndt->pointerfree = dt->pointerfree;
}
else {
jl_compute_field_offsets(ndt);
int i;
jl_compute_field_offsets(ndt, jl_emptysvec);
for (i=0; i < jl_svec_len(ftypes); i++) {
ndt->fields[i].isconst = dt->fields[i].isconst;
}
}
if (jl_is_datatype_singleton(ndt)) {
ndt->instance = newstruct(ndt);
Expand Down Expand Up @@ -3391,24 +3395,24 @@ void jl_init_types(void)
jl_svecset(jl_simplevector_type->types, 0, jl_long_type);
jl_svecset(jl_typename_type->types, 6, jl_long_type);

jl_compute_field_offsets(jl_datatype_type);
jl_compute_field_offsets(jl_typename_type);
jl_compute_field_offsets(jl_uniontype_type);
jl_compute_field_offsets(jl_tvar_type);
jl_compute_field_offsets(jl_method_type);
jl_compute_field_offsets(jl_methtable_type);
jl_compute_field_offsets(jl_expr_type);
jl_compute_field_offsets(jl_linenumbernode_type);
jl_compute_field_offsets(jl_labelnode_type);
jl_compute_field_offsets(jl_gotonode_type);
jl_compute_field_offsets(jl_quotenode_type);
jl_compute_field_offsets(jl_topnode_type);
jl_compute_field_offsets(jl_module_type);
jl_compute_field_offsets(jl_lambda_info_type);
jl_compute_field_offsets(jl_box_type);
jl_compute_field_offsets(jl_typector_type);
jl_compute_field_offsets(jl_function_type);
jl_compute_field_offsets(jl_simplevector_type);
jl_compute_field_offsets(jl_datatype_type, jl_emptysvec);
jl_compute_field_offsets(jl_typename_type, jl_emptysvec);
jl_compute_field_offsets(jl_uniontype_type, jl_emptysvec);
jl_compute_field_offsets(jl_tvar_type, jl_emptysvec);
jl_compute_field_offsets(jl_method_type, jl_emptysvec);
jl_compute_field_offsets(jl_methtable_type, jl_emptysvec);
jl_compute_field_offsets(jl_expr_type, jl_emptysvec);
jl_compute_field_offsets(jl_linenumbernode_type, jl_emptysvec);
jl_compute_field_offsets(jl_labelnode_type, jl_emptysvec);
jl_compute_field_offsets(jl_gotonode_type, jl_emptysvec);
jl_compute_field_offsets(jl_quotenode_type, jl_emptysvec);
jl_compute_field_offsets(jl_topnode_type, jl_emptysvec);
jl_compute_field_offsets(jl_module_type, jl_emptysvec);
jl_compute_field_offsets(jl_lambda_info_type, jl_emptysvec);
jl_compute_field_offsets(jl_box_type, jl_emptysvec);
jl_compute_field_offsets(jl_typector_type, jl_emptysvec);
jl_compute_field_offsets(jl_function_type, jl_emptysvec);
jl_compute_field_offsets(jl_simplevector_type, jl_emptysvec);
jl_simplevector_type->pointerfree = 0;

call_sym = jl_symbol("call");
Expand Down
7 changes: 1 addition & 6 deletions src/julia-parser.scm
Original file line number Diff line number Diff line change
Expand Up @@ -1217,12 +1217,7 @@
((break continue) (list word))
((const)
(let ((assgn (parse-eq s)))
(if (not (and (pair? assgn)
(or (eq? (car assgn) '=)
(eq? (car assgn) 'global)
(eq? (car assgn) 'local))))
(error "expected assignment after \"const\"")
`(const ,assgn))))
`(const ,assgn)))
((module baremodule)
(let* ((name (parse-unary-prefix s))
(body (parse-block s)))
Expand Down
24 changes: 19 additions & 5 deletions src/julia-syntax.scm
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@
(define (llist-types lst)
(map arg-type lst))

(define (const-decl? e)
(and (pair? e) (eq? (car e) 'const) (eq? (caadr e) '|::|)))

(define (decl? e)
(and (pair? e) (eq? (car e) '|::|)))

Expand All @@ -137,10 +140,18 @@

; get the variable name part of a declaration, x::int => x
(define (decl-var v)
(if (decl? v) (cadr v) v))
(if (decl? v)
(cadr v)
(if (const-decl? v)
(cadadr v)
v)))

(define (decl-type v)
(if (decl? v) (caddr v) 'Any))
(if (decl? v)
(caddr v)
(if (const-decl? v)
(caddr (cadr v))
'Any)))

(define (sym-dot? e)
(and (length= e 3) (eq? (car e) '|.|)
Expand Down Expand Up @@ -922,11 +933,12 @@

(define (struct-def-expr- name params bounds super fields mut)
(receive
(fields defs) (separate (lambda (x) (or (symbol? x) (decl? x)))
(fields defs) (separate (lambda (x) (or (symbol? x) (decl? x) (const-decl? x)))
fields)
(let* ((defs (filter (lambda (x) (not (effect-free? x))) defs))
(field-names (map decl-var fields))
(field-types (map decl-type fields))
(field-const (map const-decl? fields))
(defs2 (if (null? defs)
(default-inner-ctors name field-names field-types (null? params))
defs))
Expand All @@ -940,7 +952,8 @@
(global ,name) (const ,name)
(composite_type ,name (call (top svec) ,@params)
(call (top svec) ,@(map (lambda (x) `',x) field-names))
,super (call (top svec) ,@field-types) ,mut ,min-initialized)
,super (call (top svec) ,@field-types)
(call (top svec) ,@field-const) ,mut ,min-initialized)
(call
(lambda ()
(scope-block
Expand All @@ -959,7 +972,8 @@
,@(map make-assignment params (symbols->typevars params bounds #t))
(composite_type ,name (call (top svec) ,@params)
(call (top svec) ,@(map (lambda (x) `',x) field-names))
,super (call (top svec) ,@field-types) ,mut ,min-initialized)))
,super (call (top svec) ,@field-types)
(call (top svec) ,@field-const) ,mut ,min-initialized)))
;; "inner" constructors
(call
(lambda ()
Expand Down
6 changes: 4 additions & 2 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,12 +261,13 @@ typedef struct {

typedef struct {
uint16_t offset; // offset relative to data start, excluding type tag
uint16_t size:15;
uint16_t size:14;
uint16_t isconst:1;
uint16_t isptr:1;
} jl_fielddesc_t;

#define JL_FIELD_MAX_OFFSET ((1ul << 16) - 1ul)
#define JL_FIELD_MAX_SIZE ((1ul << 15) - 1ul)
#define JL_FIELD_MAX_SIZE ((1ul << 14) - 1ul)

typedef struct _jl_datatype_t {
JL_DATA_TYPE
Expand Down Expand Up @@ -739,6 +740,7 @@ STATIC_INLINE jl_value_t *jl_cellset(void *a, size_t i, void *x)
#define jl_field_offset(st,i) (((jl_datatype_t*)st)->fields[i].offset)
#define jl_field_size(st,i) (((jl_datatype_t*)st)->fields[i].size)
#define jl_field_isptr(st,i) (((jl_datatype_t*)st)->fields[i].isptr)
#define jl_field_isconst(st,i) (((jl_datatype_t*)st)->fields[i].isconst)
#define jl_field_name(st,i) (jl_sym_t*)jl_svecref(((jl_datatype_t*)st)->name->names, (i))
#define jl_field_type(st,i) jl_svecref(((jl_datatype_t*)st)->types, (i))
#define jl_datatype_size(t) (((jl_datatype_t*)t)->size)
Expand Down
2 changes: 1 addition & 1 deletion src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ void jl_initialize_generic_function(jl_function_t *f, jl_sym_t *name);
void jl_add_constructors(jl_datatype_t *t);

jl_value_t *jl_nth_slot_type(jl_tupletype_t *sig, size_t i);
void jl_compute_field_offsets(jl_datatype_t *st);
void jl_compute_field_offsets(jl_datatype_t *st, jl_svec_t *consts);
jl_array_t *jl_new_array_for_deserialization(jl_value_t *atype, uint32_t ndims, size_t *dims,
int isunboxed, int elsz);
#ifdef JL_USE_INTEL_JITEVENTS
Expand Down