Skip to content

Commit

Permalink
allow struct field access by index. closes #4806
Browse files Browse the repository at this point in the history
  • Loading branch information
JeffBezanson committed Jan 13, 2014
1 parent 8cd1702 commit bff951e
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 34 deletions.
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ New language features
* Binary `~` now parses as a vararg macro call to `@~`.
For example `x~y~z` => `@~ x y z` ([#4882]).

* Structure fields can now be accessed by index ([#4806]).

New library functions
---------------------

Expand Down Expand Up @@ -140,6 +142,7 @@ Deprecated or removed
[#2345]: https://github.com/JuliaLang/julia/issues/2345
[#5330]: https://github.com/JuliaLang/julia/issues/5330
[#4882]: https://github.com/JuliaLang/julia/issues/4882
[#4806]: https://github.com/JuliaLang/julia/issues/4806

Julia v0.2.0 Release Notes
==========================
Expand Down
6 changes: 3 additions & 3 deletions base/deepcopy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ function _deepcopy_t(x, T::DataType, stackdict::ObjectIdDict)
end
ret = ccall(:jl_new_struct_uninit, Any, (Any,), T)
stackdict[x] = ret
for f in T.names
if isdefined(x,f)
ret.(f) = deepcopy_internal(x.(f), stackdict)
for i in 1:length(T.names)
if isdefined(x,i)
ret.(i) = deepcopy_internal(x.(i), stackdict)
end
end
return ret
Expand Down
9 changes: 9 additions & 0 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,15 @@ const getfield_tfunc = function (A, s, name)
end
end
return None
elseif isa(A[2],Int)
if isa(A[1],Module) || s === Module
return None
end
i::Int = A[2]
if i < 1 || i > length(s.names)
return None
end
return s.types[i]
else
return reduce(tmerge, None, s.types)#Union(s.types...)
end
Expand Down
10 changes: 5 additions & 5 deletions base/serialize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,9 @@ function serialize(s, x)
write(s, x)
else
serialize(s, length(t.names))
for n in t.names
if isdefined(x, n)
serialize(s, getfield(x, n))
for i in 1:length(t.names)
if isdefined(x, i)
serialize(s, getfield(x, i))
else
writetag(s, UndefRefTag)
end
Expand Down Expand Up @@ -502,10 +502,10 @@ function deserialize(s, t::DataType)
end
else
x = ccall(:jl_new_struct_uninit, Any, (Any,), t)
for n in t.names
for i in 1:length(t.names)
tag = int32(read(s, Uint8))
if tag==0 || !is(deser_tag[tag], UndefRefTag)
setfield(x, n, handle_deserialize(s, tag))
setfield(x, i, handle_deserialize(s, tag))
end
end
return x
Expand Down
2 changes: 1 addition & 1 deletion base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function show(io::IO, x::ANY)
if !isdefined(x, f)
print(io, undef_ref_str)
else
show(io, x.(f))
show(io, x.(i))
end
if i < n
print(io, ',')
Expand Down
26 changes: 19 additions & 7 deletions src/alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,19 +190,22 @@ jl_value_t *jl_get_nth_field(jl_value_t *v, size_t i)
return jl_new_bits(jl_tupleref(st->types,i), (char*)v + offs);
}

int jl_field_isdefined(jl_value_t *v, jl_sym_t *fld, int err)
jl_value_t *jl_get_nth_field_checked(jl_value_t *v, size_t i)
{
jl_datatype_t *st = (jl_datatype_t*)jl_typeof(v);
int i = jl_field_index(st, fld, err);
if (i == -1) return 0;
if (i >= jl_tuple_len(st->names))
jl_throw(jl_bounds_exception);
size_t offs = jl_field_offset(st,i) + sizeof(void*);
if (st->fields[i].isptr) {
return *(jl_value_t**)((char*)v + offs) != NULL;
jl_value_t *fval = *(jl_value_t**)((char*)v + offs);
if (fval == NULL)
jl_throw(jl_undefref_exception);
return fval;
}
return 1;
return jl_new_bits(jl_tupleref(st->types,i), (char*)v + offs);
}

jl_value_t *jl_set_nth_field(jl_value_t *v, size_t i, jl_value_t *rhs)
void jl_set_nth_field(jl_value_t *v, size_t i, jl_value_t *rhs)
{
jl_datatype_t *st = (jl_datatype_t*)jl_typeof(v);
size_t offs = jl_field_offset(st,i) + sizeof(void*);
Expand All @@ -212,7 +215,16 @@ jl_value_t *jl_set_nth_field(jl_value_t *v, size_t i, jl_value_t *rhs)
else {
jl_assign_bits((char*)v + offs, rhs);
}
return rhs;
}

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) + sizeof(void*);
if (st->fields[i].isptr) {
return *(jl_value_t**)((char*)v + offs) != NULL;
}
return 1;
}

DLLEXPORT jl_value_t *jl_new_struct(jl_datatype_t *type, ...)
Expand Down
56 changes: 42 additions & 14 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -393,17 +393,29 @@ JL_CALLABLE(jl_f_isdefined)
JL_NARGS(isdefined, 1, 1);
}
else {
JL_TYPECHK(isdefined, symbol, args[1]);
s = (jl_sym_t*)args[1];
if (!jl_is_module(args[0])) {
jl_value_t *vt = (jl_value_t*)jl_typeof(args[0]);
jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(args[0]);
if (!jl_is_datatype(vt)) {
jl_type_error("isdefined", (jl_value_t*)jl_datatype_type, args[0]);
}
return jl_field_isdefined(args[0], s, 0) ? jl_true : jl_false;
size_t idx;
if (jl_is_long(args[1])) {
idx = jl_unbox_long(args[1])-1;
if (idx >= jl_tuple_len(vt->names))
return jl_false;
}
else {
JL_TYPECHK(isdefined, symbol, args[1]);
idx = jl_field_index(vt, (jl_sym_t*)args[1], 0);
if ((int)idx == -1)
return jl_false;
}
return jl_field_isdefined(args[0], idx) ? jl_true : jl_false;
}
JL_TYPECHK(isdefined, module, args[0]);
JL_TYPECHK(isdefined, symbol, args[1]);
m = (jl_module_t*)args[0];
s = (jl_sym_t*)args[1];
}
assert(s);
return jl_boundp(m, s) ? jl_true : jl_false;
Expand Down Expand Up @@ -446,18 +458,27 @@ JL_CALLABLE(jl_f_tuplelen)
JL_CALLABLE(jl_f_get_field)
{
JL_NARGS(getfield, 2, 2);
JL_TYPECHK(getfield, symbol, args[1]);
jl_value_t *v = args[0];
jl_value_t *vt = (jl_value_t*)jl_typeof(v);
if (vt == (jl_value_t*)jl_module_type) {
JL_TYPECHK(getfield, symbol, args[1]);
return jl_eval_global_var((jl_module_t*)v, (jl_sym_t*)args[1]);
}
if (!jl_is_datatype(vt)) {
if (!jl_is_datatype(vt))
jl_type_error("getfield", (jl_value_t*)jl_datatype_type, v);
}
jl_datatype_t *st = (jl_datatype_t*)vt;
jl_sym_t *fld = (jl_sym_t*)args[1];
jl_value_t *fval = jl_get_nth_field(v, jl_field_index(st, fld, 1));
size_t idx;
if (jl_is_long(args[1])) {
idx = jl_unbox_long(args[1])-1;
if (idx >= jl_tuple_len(st->names))
jl_throw(jl_bounds_exception);
}
else {
JL_TYPECHK(getfield, symbol, args[1]);
jl_sym_t *fld = (jl_sym_t*)args[1];
idx = jl_field_index(st, fld, 1);
}
jl_value_t *fval = jl_get_nth_field(v, idx);
if (fval == NULL)
jl_throw(jl_undefref_exception);
return fval;
Expand All @@ -466,7 +487,6 @@ JL_CALLABLE(jl_f_get_field)
JL_CALLABLE(jl_f_set_field)
{
JL_NARGS(setfield, 3, 3);
JL_TYPECHK(setfield, symbol, args[1]);
jl_value_t *v = args[0];
jl_value_t *vt = (jl_value_t*)jl_typeof(v);
if (vt == (jl_value_t*)jl_module_type)
Expand All @@ -476,13 +496,21 @@ JL_CALLABLE(jl_f_set_field)
jl_datatype_t *st = (jl_datatype_t*)vt;
if (!st->mutabl)
jl_errorf("type %s is immutable", st->name->name->name);
jl_sym_t *fld = (jl_sym_t*)args[1];
size_t i = jl_field_index(st, fld, 1);
jl_value_t *ft = jl_tupleref(st->types,i);
size_t idx;
if (jl_is_long(args[1])) {
idx = jl_unbox_long(args[1])-1;
if (idx >= jl_tuple_len(st->names))
jl_throw(jl_bounds_exception);
}
else {
JL_TYPECHK(setfield, symbol, args[1]);
idx = jl_field_index(st, (jl_sym_t*)args[1], 1);
}
jl_value_t *ft = jl_tupleref(st->types, idx);
if (!jl_subtype(args[2], ft, 1)) {
jl_type_error("setfield", ft, args[2]);
}
jl_set_nth_field(v, i, args[2]);
jl_set_nth_field(v, idx, args[2]);
return args[2];
}

Expand Down
111 changes: 111 additions & 0 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ static Function *box64_func;
static Function *jlputs_func;
static Function *jldlsym_func;
static Function *jlnewbits_func;
//static Function *jlgetnthfield_func;
static Function *jlgetnthfieldchecked_func;
//static Function *jlsetnthfield_func;
#ifdef _OS_WINDOWS_
static Function *resetstkoflw_func;
#endif
Expand Down Expand Up @@ -1227,6 +1230,33 @@ static Value *emit_f_is(jl_value_t *rt1, jl_value_t *rt2,
return answer;
}

static bool is_structtype_all_pointers(jl_datatype_t *dt)
{
jl_tuple_t *t = dt->types;
size_t i, l = jl_tuple_len(t);
for(i=0; i < l; i++) {
if (!dt->fields[i].isptr)
return false;
}
return true;
}

static bool is_structtype_homogeneous(jl_datatype_t *dt)
{
jl_tuple_t *t = dt->types;
size_t i, l = jl_tuple_len(t);
if (l > 0) {
jl_value_t *t0 = jl_tupleref(t, 0);
if (!jl_is_leaf_type(t0))
return false;
for(i=1; i < l; i++) {
if (!jl_types_equal(t0, jl_tupleref(t,i)))
return false;
}
}
return true;
}

static Value *emit_known_call(jl_value_t *ff, jl_value_t **args, size_t nargs,
jl_codectx_t *ctx,
Value **theFptr, jl_function_t **theF,
Expand Down Expand Up @@ -1675,6 +1705,77 @@ static Value *emit_known_call(jl_value_t *ff, jl_value_t **args, size_t nargs,
JL_GC_POP();
return fld;
}
jl_datatype_t *stt = (jl_datatype_t*)expr_type(args[1], ctx);
jl_value_t *fldt = expr_type(args[2], ctx);
if (jl_is_structtype(stt) && fldt == (jl_value_t*)jl_long_type && !jl_subtype((jl_value_t*)jl_module_type, (jl_value_t*)stt, 0)) {
size_t nfields = jl_tuple_len(stt->names);
// integer index
if (jl_is_long(args[2])) {
// known index
size_t idx = jl_unbox_long(args[2])-1;
if (idx < nfields) {
Value *fld = emit_getfield(args[1],
(jl_sym_t*)jl_tupleref(stt->names, idx),
ctx);
JL_GC_POP();
return fld;
}
}
else {
// unknown index
Value *strct = emit_expr(args[1], ctx);
Value *idx = emit_unbox(T_size, emit_unboxed(args[2], ctx), (jl_value_t*)jl_long_type);
Type *llvm_st = strct->getType();
if (llvm_st == jl_pvalue_llvmt) {
if (is_structtype_all_pointers(stt)) {
idx = emit_bounds_check(idx, ConstantInt::get(T_size, nfields), ctx);
Value *fld =
builder.
CreateLoad(builder.
CreateGEP(builder.
CreateBitCast(strct, jl_ppvalue_llvmt),
builder.CreateAdd(idx,ConstantInt::get(T_size,1))));
null_pointer_check(fld, ctx);
JL_GC_POP();
return fld;
}
else if (is_structtype_homogeneous(stt)) {
assert(nfields > 0); // nf==0 trapped by all_pointers case
jl_value_t *jt = jl_t0(stt->types);
idx = emit_bounds_check(idx, ConstantInt::get(T_size, nfields), ctx);
Value *ptr = data_pointer(strct);
JL_GC_POP();
return typed_load(ptr, idx, jt, ctx);
}
else {
idx = builder.CreateSub(idx, ConstantInt::get(T_size, 1));
Value *fld = builder.CreateCall2(jlgetnthfieldchecked_func, strct, idx);
JL_GC_POP();
return fld;
}
}
else if (is_structtype_homogeneous(stt)) {
assert(llvm_st->isStructTy());
// TODO: move these allocas to the first basic block instead of
// frobbing the stack

This comment has been minimized.

Copy link
@vtjnash

vtjnash Jan 13, 2014

Member

is that a word? google search results seem to indicate this means julia now acts like a typical teenager

Urban Dictionary: frobbing
www.urbandictionary.com/define.php?term=frobbing‎
To spend excessive time on a computer surfing the net ...

This comment has been minimized.

Copy link
@JeffBezanson

JeffBezanson Jan 13, 2014

Author Member

That's not a meaning I'm aware of. Check the hacker's dictionary.

This comment has been minimized.

Copy link
@jiahao

jiahao Jan 13, 2014

Member

Presumably this isn't the OED definition either, which is as a synonym of 'throb':

1542 My hart frobbed exceedingly.

This comment has been minimized.

Copy link
@JeffBezanson

JeffBezanson Jan 13, 2014

Author Member

Closer?

This comment has been minimized.

Copy link
@amitmurthy
Instruction *stacksave =
CallInst::Create(Intrinsic::getDeclaration(jl_Module,
Intrinsic::stacksave));
builder.Insert(stacksave);
Value *tempSpace = builder.CreateAlloca(llvm_st);
builder.CreateStore(strct, tempSpace);
jl_value_t *jt = jl_t0(stt->types);
idx = emit_bounds_check(idx, ConstantInt::get(T_size, nfields), ctx);
Value *ptr = builder.CreateGEP(tempSpace, ConstantInt::get(T_size, 0));
Value *fld = typed_load(ptr, idx, jt, ctx);
builder.CreateCall(Intrinsic::getDeclaration(jl_Module,
Intrinsic::stackrestore),
stacksave);
JL_GC_POP();
return fld;
}
}
}
}
else if (f->fptr == &jl_f_set_field && nargs==3) {
jl_datatype_t *sty = (jl_datatype_t*)expr_type(args[1], ctx);
Expand All @@ -1701,6 +1802,7 @@ static Value *emit_known_call(jl_value_t *ff, jl_value_t **args, size_t nargs,
}
}
}
// TODO: faster code for integer index
}
else if (f->fptr == &jl_f_instantiate_type && nargs > 0) {
size_t i;
Expand Down Expand Up @@ -3752,6 +3854,15 @@ static void init_julia_llvm_env(Module *m)
"jl_new_bits", jl_Module);
jl_ExecutionEngine->addGlobalMapping(jlnewbits_func, (void*)&jl_new_bits);

std::vector<Type *> getnthfld_args(0);
getnthfld_args.push_back(jl_pvalue_llvmt);
getnthfld_args.push_back(T_size);
jlgetnthfieldchecked_func =
Function::Create(FunctionType::get(jl_pvalue_llvmt, getnthfld_args, false),
Function::ExternalLinkage,
"jl_get_nth_field_checked", jl_Module);
jl_ExecutionEngine->addGlobalMapping(jlgetnthfieldchecked_func, (void*)*jl_get_nth_field_checked);

// set up optimization passes
FPM = new FunctionPassManager(jl_Module);

Expand Down
5 changes: 3 additions & 2 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -778,8 +778,9 @@ DLLEXPORT void *jl_unbox_voidpointer(jl_value_t *v);
void jl_compute_field_offsets(jl_datatype_t *st);
int jl_field_index(jl_datatype_t *t, jl_sym_t *fld, int err);
DLLEXPORT jl_value_t *jl_get_nth_field(jl_value_t *v, size_t i);
jl_value_t *jl_set_nth_field(jl_value_t *v, size_t i, jl_value_t *rhs);
int jl_field_isdefined(jl_value_t *v, jl_sym_t *fld, int err);
DLLEXPORT jl_value_t *jl_get_nth_field_checked(jl_value_t *v, size_t i);
DLLEXPORT void jl_set_nth_field(jl_value_t *v, size_t i, jl_value_t *rhs);
DLLEXPORT int jl_field_isdefined(jl_value_t *v, size_t i);

// arrays
DLLEXPORT jl_array_t *jl_new_array(jl_value_t *atype, jl_tuple_t *dims);
Expand Down
Loading

0 comments on commit bff951e

Please sign in to comment.