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: add support for working with immutables (#11902) #12113

Closed
wants to merge 3 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
2 changes: 1 addition & 1 deletion base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export
Expr, GotoNode, LabelNode, LineNumberNode, QuoteNode, SymbolNode, TopNode,
GlobalRef, NewvarNode, GenSym,
# object model functions
fieldtype, getfield, setfield!, nfields, throw, tuple, is, ===, isdefined,
fieldtype, getfield, setfield!, modifyelement, nfields, throw, tuple, is, ===, isdefined,
# arraylen, arrayref, arrayset, arraysize,
# _apply, kwcall,
# sizeof # not exported, to avoid conflicting with Base.sizeof
Expand Down
1 change: 1 addition & 0 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ const getfield_tfunc = function (A, s0, name)
end
add_tfunc(getfield, 2, 2, (A,s,name)->getfield_tfunc(A,s,name)[1])
add_tfunc(setfield!, 3, 3, (o, f, v)->v)
add_tfunc(modifyelement, 3, 3, (args...)->Void)
const fieldtype_tfunc = function (A, s, name)
if isType(s)
s = s.parameters[1]
Expand Down
5 changes: 5 additions & 0 deletions base/refpointer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ function unsafe_convert(P::Type{Ptr{Any}}, b::RefValue{Any})
end
unsafe_convert{T}(::Type{Ptr{Void}}, b::RefValue{T}) = convert(Ptr{Void}, unsafe_convert(Ptr{T}, b))

function setindex!{T<:Tuple}(x::RefValue{T}, val, idxs...)
Core.modifyelement(x, idxs, val)
val
end

### Methods for a Ref object that is backed by an array at index i
immutable RefArray{T, A<:AbstractArray, R} <: Ref{T}
x::A
Expand Down
1 change: 1 addition & 0 deletions src/builtin_proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ JL_CALLABLE(jl_f_tuple);
JL_CALLABLE(jl_f_svec);
JL_CALLABLE(jl_f_get_field);
JL_CALLABLE(jl_f_set_field);
JL_CALLABLE(jl_f_modifyelement);
JL_CALLABLE(jl_f_field_type);
JL_CALLABLE(jl_f_arraylen);
JL_CALLABLE(jl_f_arrayref);
Expand Down
52 changes: 52 additions & 0 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,57 @@ JL_CALLABLE(jl_f_set_field)
return args[2];
}

static uint8_t *_getelmentptr(uint8_t *ptr, size_t idx, jl_datatype_t *st)
{
if (idx >= jl_datatype_nfields(st))
jl_bounds_error_int((jl_value_t*)st, idx+1);
return (uint8_t*)(ptr + jl_field_offset(st,idx));
}

JL_CALLABLE(jl_f_modifyelement)
{
JL_NARGS(modifyelement, 3, 3)
jl_value_t *v = args[0];
jl_value_t *vt = jl_typeof(v);
if (!jl_is_ref_type(vt))
jl_type_error("modifyelement", (jl_value_t*)jl_ref_type, v);
jl_value_t *tt = jl_tparam0(vt);
if (!jl_is_datatype(tt))
jl_type_error("modifyelement", (jl_value_t*)jl_datatype_type, v);
if (!jl_is_tuple_type(tt))
jl_type_error("modifyelement", (jl_value_t*)jl_tuple_type, v);
if (!jl_isbits(tt))
jl_error("modifyelement currently only applies to isbits tuples");
jl_datatype_t *st = (jl_datatype_t*)tt;
uint8_t *ptr = (uint8_t*)v;
jl_value_t *tpl_type = (jl_value_t*)st;
if (jl_is_long(args[1])) {
size_t idx = jl_unbox_long(args[1])-1;
ptr = _getelmentptr(ptr, idx, st);
tpl_type = jl_field_type(tpl_type, idx);
} else {
jl_value_t *idxval = NULL;
JL_TYPECHK(modifyelement, tuple, args[1]);
JL_GC_PUSH2(&idxval, &tpl_type);
for (int idx = 0; idx < jl_nfields(args[1]); ++idx) {
idxval = jl_get_nth_field(args[1], idx);
if (!jl_is_long(idxval))
jl_error("modifyelement can only take Ints or tuples thereof");
size_t tplidx = jl_unbox_long(idxval)-1;
if (!jl_is_datatype(tpl_type))
jl_type_error("modifyelement", (jl_value_t*)jl_datatype_type, tpl_type);
ptr = _getelmentptr(ptr, tplidx, (jl_datatype_t*)tpl_type);
tpl_type = jl_field_type(tpl_type, tplidx);
}
JL_GC_POP();
}
if (jl_typeof(args[2]) != tpl_type)
jl_type_error("modifyelement", (jl_value_t*)tpl_type, args[2]);
jl_assign_bits(ptr,args[2]);
jl_gc_wb(args[0],args[2]);
return jl_nothing;
}

JL_CALLABLE(jl_f_field_type)
{
JL_NARGS(fieldtype, 2, 2);
Expand Down Expand Up @@ -1186,6 +1237,7 @@ void jl_init_primitives(void)
// functions for internal use
add_builtin_func("getfield", jl_f_get_field);
add_builtin_func("setfield!", jl_f_set_field);
add_builtin_func("modifyelement", jl_f_modifyelement);
add_builtin_func("fieldtype", jl_f_field_type);
add_builtin_func("nfields", jl_f_nfields);
add_builtin_func("_expr", jl_f_new_expr);
Expand Down
93 changes: 59 additions & 34 deletions src/cgutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1091,9 +1091,9 @@ static Value *emit_bounds_check(Value *a, jl_value_t *ty, Value *i, Value *len,
}
else {
#ifdef LLVM37
builder.CreateCall(prepare_call(jlboundserror_func), { a, i });
builder.CreateCall(prepare_call(jlboundserrorint_func), { a, i });
#else
builder.CreateCall2(prepare_call(jlboundserror_func), a, i);
builder.CreateCall2(prepare_call(jlboundserrorint_func), a, i);
#endif
}
builder.CreateUnreachable();
Expand Down Expand Up @@ -1363,6 +1363,39 @@ static Value *emit_getfield_unknownidx(Value *strct, Value *idx, jl_datatype_t *
return NULL;
}

static Value *emit_gep_knownidx(Value *strct, ArrayRef<Value *> idxs, jl_datatype_t *jt)
{
Type *strctty = julia_struct_to_llvm((jl_value_t*)jt);
if (strct->getType() == jl_pvalue_llvmt)
strct = builder.CreateBitCast(strct, PointerType::get(strctty,0));
#ifdef LLVM37
return builder.CreateInBoundsGEP(strctty, strct, idxs);
#else
return builder.CreateInBoundsGEP(strct, idxs);
#endif
}

static Value *emit_gep_knownidx(Value *strct, unsigned idx, jl_datatype_t *jt)
{
Value *addr;
if (strct->getType() == jl_pvalue_llvmt) {
addr = builder.CreateGEP(builder.CreateBitCast(strct, T_pint8),
ConstantInt::get(T_size, jl_field_offset(jt,idx)));
}
else if (strct->getType()->isPointerTy()) { // something stack allocated
# ifdef LLVM37
addr = builder.CreateConstInBoundsGEP2_32(
cast<PointerType>(strct->getType()->getScalarType())->getElementType(),
strct, 0, idx);
# else
addr = builder.CreateConstInBoundsGEP2_32(strct, 0, idx);
# endif
} else {
assert(false && "Cannot call GEP on this");
}
return addr;
}

static Value *emit_getfield_knownidx(Value *strct, unsigned idx, jl_datatype_t *jt, jl_codectx_t *ctx)
{
jl_value_t *jfty = jl_field_type(jt,idx);
Expand All @@ -1375,39 +1408,31 @@ static Value *emit_getfield_knownidx(Value *strct, unsigned idx, jl_datatype_t *
if (elty == T_void)
return ghostValue(jfty);
Value *fldv = NULL;
if (strct->getType() == jl_pvalue_llvmt) {
Value *addr =
builder.CreateGEP(builder.CreateBitCast(strct, T_pint8),
ConstantInt::get(T_size, jl_field_offset(jt,idx)));
MDNode *tbaa = jt->mutabl ? tbaa_user : tbaa_immut;
if (jl_field_isptr(jt,idx)) {
Value *fldv = tbaa_decorate(tbaa, builder.CreateLoad(builder.CreateBitCast(addr,jl_ppvalue_llvmt)));
if (idx >= (unsigned)jt->ninitialized)
null_pointer_check(fldv, ctx);
return fldv;
}
else {
int align = jl_field_offset(jt,idx);
if (align & 1) align = 1;
else if (align & 2) align = 2;
else if (align & 4) align = 4;
else if (align & 8) align = 8;
else align = 16;
return typed_load(addr, ConstantInt::get(T_size, 0), jfty, ctx, tbaa, align);

if (strct->getType() == jl_pvalue_llvmt || strct->getType()->isPointerTy()) {
Value *addr = emit_gep_knownidx(strct, idx, jt);
if (strct->getType() == jl_pvalue_llvmt) {
MDNode *tbaa = jt->mutabl ? tbaa_user : tbaa_immut;
if (jl_field_isptr(jt,idx)) {
Value *fldv = tbaa_decorate(tbaa, builder.CreateLoad(builder.CreateBitCast(addr,jl_ppvalue_llvmt)));
if (idx >= (unsigned)jt->ninitialized)
null_pointer_check(fldv, ctx);
return fldv;
}
else {
int align = jl_field_offset(jt,idx);
if (align & 1) align = 1;
else if (align & 2) align = 2;
else if (align & 4) align = 4;
else if (align & 8) align = 8;
else align = 16;
return typed_load(addr, ConstantInt::get(T_size, 0), jfty, ctx, tbaa, align);
}
} else {
assert(!jt->mutabl);
return typed_load(addr, NULL, jfty, ctx, NULL);
}
}
else if (strct->getType()->isPointerTy()) { // something stack allocated
# ifdef LLVM37
Value *addr = builder.CreateConstInBoundsGEP2_32(
cast<PointerType>(strct->getType()->getScalarType())->getElementType(),
strct, 0, idx);
# else
Value *addr = builder.CreateConstInBoundsGEP2_32(strct, 0, idx);
# endif
assert(!jt->mutabl);
return typed_load(addr, NULL, jfty, ctx, NULL);
}
else {
} else {
assert(strct->getType()->isVectorTy());
fldv = builder.CreateExtractElement(strct, ConstantInt::get(T_int32, idx));
if (jfty == (jl_value_t*)jl_bool_type) {
Expand Down
Loading