Skip to content

Commit bb1260f

Browse files
committed
WIP: Implement #11902
This is WIP towards #11902. Only bits tuples are supported, and this has only so far been tested on Mac/LLVM37
1 parent b5fed62 commit bb1260f

File tree

9 files changed

+272
-44
lines changed

9 files changed

+272
-44
lines changed

base/boot.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ export
146146
Expr, GotoNode, LabelNode, LineNumberNode, QuoteNode, SymbolNode, TopNode,
147147
GlobalRef, NewvarNode, GenSym,
148148
# object model functions
149-
fieldtype, getfield, setfield!, nfields, throw, tuple, is, ===, isdefined,
149+
fieldtype, getfield, setfield!, modifyelement, nfields, throw, tuple, is, ===, isdefined,
150150
# arraylen, arrayref, arrayset, arraysize,
151151
# _apply, kwcall,
152152
# sizeof # not exported, to avoid conflicting with Base.sizeof

base/inference.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ const getfield_tfunc = function (A, s0, name)
321321
end
322322
add_tfunc(getfield, 2, 2, (A,s,name)->getfield_tfunc(A,s,name)[1])
323323
add_tfunc(setfield!, 3, 3, (o, f, v)->v)
324+
add_tfunc(modifyelement, 3, 3, (args...)->Void)
324325
const fieldtype_tfunc = function (A, s, name)
325326
if isType(s)
326327
s = s.parameters[1]

base/refpointer.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ function unsafe_convert(P::Type{Ptr{Any}}, b::RefValue{Any})
3737
end
3838
unsafe_convert{T}(::Type{Ptr{Void}}, b::RefValue{T}) = convert(Ptr{Void}, unsafe_convert(Ptr{T}, b))
3939

40+
function setindex!{T<:Tuple}(x::RefValue{T}, val, idxs...)
41+
Core.modifyelement(x, idxs, val)
42+
val
43+
end
44+
4045
### Methods for a Ref object that is backed by an array at index i
4146
immutable RefArray{T, A<:AbstractArray, R} <: Ref{T}
4247
x::A

src/builtin_proto.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ JL_CALLABLE(jl_f_tuple);
2424
JL_CALLABLE(jl_f_svec);
2525
JL_CALLABLE(jl_f_get_field);
2626
JL_CALLABLE(jl_f_set_field);
27+
JL_CALLABLE(jl_f_modifyelement);
2728
JL_CALLABLE(jl_f_field_type);
2829
JL_CALLABLE(jl_f_arraylen);
2930
JL_CALLABLE(jl_f_arrayref);

src/builtins.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,57 @@ JL_CALLABLE(jl_f_set_field)
717717
return args[2];
718718
}
719719

720+
static uint8_t *_getelmentptr(uint8_t *ptr, size_t idx, jl_datatype_t *st)
721+
{
722+
if (idx >= jl_datatype_nfields(st))
723+
jl_bounds_error_int((jl_value_t*)st, idx+1);
724+
return (uint8_t*)(ptr + jl_field_offset(st,idx));
725+
}
726+
727+
JL_CALLABLE(jl_f_modifyelement)
728+
{
729+
JL_NARGS(modifyelement, 3, 3)
730+
jl_value_t *v = args[0];
731+
jl_value_t *vt = jl_typeof(v);
732+
if (!jl_is_ref_type(vt))
733+
jl_type_error("modifyelement", (jl_value_t*)jl_ref_type, v);
734+
jl_value_t *tt = jl_tparam0(vt);
735+
if (!jl_is_datatype(tt))
736+
jl_type_error("modifyelement", (jl_value_t*)jl_datatype_type, v);
737+
if (!jl_is_tuple_type(tt))
738+
jl_type_error("modifyelement", (jl_value_t*)jl_tuple_type, v);
739+
if (!jl_isbits(tt))
740+
jl_error("modifyelement currently only applies to isbits tuples");
741+
jl_datatype_t *st = (jl_datatype_t*)tt;
742+
uint8_t *ptr = (uint8_t*)v;
743+
jl_value_t *tpl_type = (jl_value_t*)st;
744+
if (jl_is_long(args[1])) {
745+
size_t idx = jl_unbox_long(args[1])-1;
746+
ptr = _getelmentptr(ptr, idx, st);
747+
tpl_type = jl_field_type(tpl_type, idx);
748+
} else {
749+
jl_value_t *idxval = NULL;
750+
JL_TYPECHK(modifyelement, tuple, args[1]);
751+
JL_GC_PUSH2(&idxval, &tpl_type);
752+
for (int idx = 0; idx < jl_nfields(args[1]); ++idx) {
753+
idxval = jl_get_nth_field(args[1], idx);
754+
if (!jl_is_long(idxval))
755+
jl_error("modifyelement can only take Ints or tuples thereof");
756+
size_t tplidx = jl_unbox_long(idxval)-1;
757+
if (!jl_is_datatype(tpl_type))
758+
jl_type_error("modifyelement", (jl_value_t*)jl_datatype_type, tpl_type);
759+
ptr = _getelmentptr(ptr, tplidx, (jl_datatype_t*)tpl_type);
760+
tpl_type = jl_field_type(tpl_type, tplidx);
761+
}
762+
JL_GC_POP();
763+
}
764+
if (jl_typeof(args[2]) != tpl_type)
765+
jl_type_error("modifyelement", (jl_value_t*)tpl_type, args[2]);
766+
jl_assign_bits(ptr,args[2]);
767+
jl_gc_wb(args[0],args[2]);
768+
return jl_nothing;
769+
}
770+
720771
JL_CALLABLE(jl_f_field_type)
721772
{
722773
JL_NARGS(fieldtype, 2, 2);
@@ -1186,6 +1237,7 @@ void jl_init_primitives(void)
11861237
// functions for internal use
11871238
add_builtin_func("getfield", jl_f_get_field);
11881239
add_builtin_func("setfield!", jl_f_set_field);
1240+
add_builtin_func("modifyelement", jl_f_modifyelement);
11891241
add_builtin_func("fieldtype", jl_f_field_type);
11901242
add_builtin_func("nfields", jl_f_nfields);
11911243
add_builtin_func("_expr", jl_f_new_expr);

src/cgutils.cpp

Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,9 +1091,9 @@ static Value *emit_bounds_check(Value *a, jl_value_t *ty, Value *i, Value *len,
10911091
}
10921092
else {
10931093
#ifdef LLVM37
1094-
builder.CreateCall(prepare_call(jlboundserror_func), { a, i });
1094+
builder.CreateCall(prepare_call(jlboundserrorint_func), { a, i });
10951095
#else
1096-
builder.CreateCall2(prepare_call(jlboundserror_func), a, i);
1096+
builder.CreateCall2(prepare_call(jlboundserrorint_func), a, i);
10971097
#endif
10981098
}
10991099
builder.CreateUnreachable();
@@ -1363,6 +1363,35 @@ static Value *emit_getfield_unknownidx(Value *strct, Value *idx, jl_datatype_t *
13631363
return NULL;
13641364
}
13651365

1366+
static Value *emit_gep_knownidx(Value *strct, ArrayRef<Value *> idxs, jl_datatype_t *jt)
1367+
{
1368+
Type *strctty = julia_struct_to_llvm((jl_value_t*)jt);
1369+
if (strct->getType() == jl_pvalue_llvmt)
1370+
strct = builder.CreateBitCast(strct, PointerType::get(strctty,0));
1371+
return builder.CreateInBoundsGEP(strctty, strct, idxs);
1372+
}
1373+
1374+
static Value *emit_gep_knownidx(Value *strct, unsigned idx, jl_datatype_t *jt)
1375+
{
1376+
Value *addr;
1377+
if (strct->getType() == jl_pvalue_llvmt) {
1378+
addr = builder.CreateGEP(builder.CreateBitCast(strct, T_pint8),
1379+
ConstantInt::get(T_size, jl_field_offset(jt,idx)));
1380+
}
1381+
else if (strct->getType()->isPointerTy()) { // something stack allocated
1382+
# ifdef LLVM37
1383+
addr = builder.CreateConstInBoundsGEP2_32(
1384+
cast<PointerType>(strct->getType()->getScalarType())->getElementType(),
1385+
strct, 0, idx);
1386+
# else
1387+
addr = builder.CreateConstInBoundsGEP2_32(strct, 0, idx);
1388+
# endif
1389+
} else {
1390+
assert(false && "Cannot call GEP on this");
1391+
}
1392+
return addr;
1393+
}
1394+
13661395
static Value *emit_getfield_knownidx(Value *strct, unsigned idx, jl_datatype_t *jt, jl_codectx_t *ctx)
13671396
{
13681397
jl_value_t *jfty = jl_field_type(jt,idx);
@@ -1375,39 +1404,31 @@ static Value *emit_getfield_knownidx(Value *strct, unsigned idx, jl_datatype_t *
13751404
if (elty == T_void)
13761405
return ghostValue(jfty);
13771406
Value *fldv = NULL;
1378-
if (strct->getType() == jl_pvalue_llvmt) {
1379-
Value *addr =
1380-
builder.CreateGEP(builder.CreateBitCast(strct, T_pint8),
1381-
ConstantInt::get(T_size, jl_field_offset(jt,idx)));
1382-
MDNode *tbaa = jt->mutabl ? tbaa_user : tbaa_immut;
1383-
if (jl_field_isptr(jt,idx)) {
1384-
Value *fldv = tbaa_decorate(tbaa, builder.CreateLoad(builder.CreateBitCast(addr,jl_ppvalue_llvmt)));
1385-
if (idx >= (unsigned)jt->ninitialized)
1386-
null_pointer_check(fldv, ctx);
1387-
return fldv;
1388-
}
1389-
else {
1390-
int align = jl_field_offset(jt,idx);
1391-
if (align & 1) align = 1;
1392-
else if (align & 2) align = 2;
1393-
else if (align & 4) align = 4;
1394-
else if (align & 8) align = 8;
1395-
else align = 16;
1396-
return typed_load(addr, ConstantInt::get(T_size, 0), jfty, ctx, tbaa, align);
1407+
1408+
if (strct->getType() == jl_pvalue_llvmt || strct->getType()->isPointerTy()) {
1409+
Value *addr = emit_gep_knownidx(strct, idx, jt);
1410+
if (strct->getType() == jl_pvalue_llvmt) {
1411+
MDNode *tbaa = jt->mutabl ? tbaa_user : tbaa_immut;
1412+
if (jl_field_isptr(jt,idx)) {
1413+
Value *fldv = tbaa_decorate(tbaa, builder.CreateLoad(builder.CreateBitCast(addr,jl_ppvalue_llvmt)));
1414+
if (idx >= (unsigned)jt->ninitialized)
1415+
null_pointer_check(fldv, ctx);
1416+
return fldv;
1417+
}
1418+
else {
1419+
int align = jl_field_offset(jt,idx);
1420+
if (align & 1) align = 1;
1421+
else if (align & 2) align = 2;
1422+
else if (align & 4) align = 4;
1423+
else if (align & 8) align = 8;
1424+
else align = 16;
1425+
return typed_load(addr, ConstantInt::get(T_size, 0), jfty, ctx, tbaa, align);
1426+
}
1427+
} else {
1428+
assert(!jt->mutabl);
1429+
return typed_load(addr, NULL, jfty, ctx, NULL);
13971430
}
1398-
}
1399-
else if (strct->getType()->isPointerTy()) { // something stack allocated
1400-
# ifdef LLVM37
1401-
Value *addr = builder.CreateConstInBoundsGEP2_32(
1402-
cast<PointerType>(strct->getType()->getScalarType())->getElementType(),
1403-
strct, 0, idx);
1404-
# else
1405-
Value *addr = builder.CreateConstInBoundsGEP2_32(strct, 0, idx);
1406-
# endif
1407-
assert(!jt->mutabl);
1408-
return typed_load(addr, NULL, jfty, ctx, NULL);
1409-
}
1410-
else {
1431+
} else {
14111432
assert(strct->getType()->isVectorTy());
14121433
fldv = builder.CreateExtractElement(strct, ConstantInt::get(T_int32, idx));
14131434
if (jfty == (jl_value_t*)jl_bool_type) {

src/codegen.cpp

Lines changed: 136 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ static Function *jlthrow_line_func;
288288
static Function *jlerror_func;
289289
static Function *jltypeerror_func;
290290
static Function *jlundefvarerror_func;
291+
static Function *jlboundserrorint_func;
291292
static Function *jlboundserror_func;
292293
static Function *jluboundserror_func;
293294
static Function *jlvboundserror_func;
@@ -2488,6 +2489,123 @@ static Value *emit_known_call(jl_value_t *ff, jl_value_t **args, size_t nargs,
24882489
}
24892490
// TODO: faster code for integer index
24902491
}
2492+
else if (f->fptr == &jl_f_modifyelement && nargs == 3) {
2493+
jl_datatype_t *sty = (jl_datatype_t*)expr_type(args[1], ctx);
2494+
// First check if this is a constant index
2495+
bool isconstidx = jl_is_long(args[2]);
2496+
if (!isconstidx && jl_is_tuple(args[2])) {
2497+
isconstidx = true;
2498+
jl_datatype_t *tt = (jl_datatype_t*)jl_typeof(args[2]);
2499+
for (size_t i = 0; i < jl_nfields(args[2]); ++i) {
2500+
if ((jl_datatype_t*)jl_field_type(tt, i) != jl_long_type) {
2501+
isconstidx = false;
2502+
break;
2503+
}
2504+
}
2505+
}
2506+
2507+
rt1 = (jl_value_t*)sty;
2508+
if (jl_is_ref_type((jl_value_t*)sty)) {
2509+
jl_datatype_t *tt = (jl_datatype_t*)jl_tparam0(sty);
2510+
if (jl_is_tuple_type(tt)){
2511+
Value *addr = NULL;
2512+
Value *strct = NULL;
2513+
jl_datatype_t *fieldty;
2514+
if (isconstidx) {
2515+
if (jl_is_long(args[2])) {
2516+
size_t idx = jl_unbox_long(args[2]) - 1;
2517+
if (idx < jl_datatype_nfields(sty)) {
2518+
Value *strct = emit_expr(args[1], ctx);
2519+
addr = emit_gep_knownidx(strct, idx, sty);
2520+
fieldty = (jl_datatype_t*)jl_field_type(tt, idx);
2521+
}
2522+
}
2523+
else {
2524+
// First check bounds, then emit code
2525+
bool boundsok = true;
2526+
fieldty = tt;
2527+
for (size_t i = 0; i < jl_nfields(args[2]); ++i) {
2528+
unsigned idx = jl_unbox_long(jl_get_nth_field(args[2],i))-1;
2529+
if (idx >= jl_datatype_nfields(fieldty)) {
2530+
boundsok = false;
2531+
break;
2532+
}
2533+
fieldty = (jl_datatype_t*)jl_field_type(fieldty, idx);
2534+
}
2535+
if (boundsok) {
2536+
Value *strct = emit_expr(args[1], ctx);
2537+
std::vector<Value *> idxs;
2538+
idxs.push_back(ConstantInt::get(T_int32, 0));
2539+
idxs.push_back(ConstantInt::get(T_int32, 0));
2540+
for (size_t i = 0; i < jl_nfields(args[2]); ++i)
2541+
idxs.push_back(ConstantInt::get(T_int32, jl_unbox_long(jl_get_nth_field(args[2],i))-1));
2542+
addr = emit_gep_knownidx(strct, idxs, sty);
2543+
}
2544+
}
2545+
} else {
2546+
// Not const index, first check if the tuple is homogeneous to the required depth
2547+
jl_datatype_t *idxty = (jl_datatype_t*)expr_type(args[2], ctx);
2548+
int depth = idxty == jl_long_type ? 1 : jl_is_tuple_type(idxty) ?
2549+
jl_svec_len(idxty->types) : 0;
2550+
if (depth != 0) {
2551+
fieldty = tt;
2552+
bool ishomogeneous = true;
2553+
for (size_t i = 0; i < depth; ++i) {
2554+
if (!is_tupletype_homogeneous(fieldty->types)) {
2555+
ishomogeneous = false;
2556+
break;
2557+
}
2558+
fieldty = (jl_datatype_t*)jl_field_type(fieldty,0);
2559+
}
2560+
if (ishomogeneous) {
2561+
// Now we know we can emit this efficiently
2562+
strct = emit_expr(args[1], ctx);
2563+
Value *idxval = emit_expr(args[2], ctx);
2564+
// Emit boundscheck
2565+
std::vector<Value *> idxs;
2566+
idxs.push_back(ConstantInt::get(T_int32, 0));
2567+
idxs.push_back(ConstantInt::get(T_int32, 0));
2568+
fieldty = tt;
2569+
Value *bndacc = ConstantInt::get(T_int1, 1);
2570+
for (size_t i = 0; i < depth; ++i) {
2571+
Value *idx = builder.CreateSub(
2572+
emit_getfield_knownidx(idxval, i, idxty, ctx),
2573+
ConstantInt::get(T_size, 1));
2574+
// For bounds check
2575+
Value *ok = builder.CreateICmpULT(idx,
2576+
ConstantInt::get(T_size, jl_datatype_nfields(fieldty)));
2577+
bndacc = builder.CreateAnd(ok,bndacc);
2578+
idxs.push_back(idx);
2579+
fieldty = (jl_datatype_t*)jl_field_type(fieldty,0);
2580+
}
2581+
if (((ctx->boundsCheck.empty() || ctx->boundsCheck.back()==true) &&
2582+
jl_options.check_bounds != JL_OPTIONS_CHECK_BOUNDS_OFF) ||
2583+
jl_options.check_bounds == JL_OPTIONS_CHECK_BOUNDS_ON) {
2584+
BasicBlock *failBB = BasicBlock::Create(getGlobalContext(),"fail",ctx->f);
2585+
BasicBlock *passBB = BasicBlock::Create(getGlobalContext(),"pass",ctx->f);
2586+
builder.CreateCondBr(bndacc, passBB, failBB);
2587+
builder.SetInsertPoint(failBB);
2588+
builder.CreateCall(prepare_call(jlboundserror_func), {
2589+
boxed(strct, ctx), boxed(idxval, ctx, (jl_value_t*)idxty)
2590+
});
2591+
builder.CreateUnreachable();
2592+
builder.SetInsertPoint(passBB);
2593+
}
2594+
addr = emit_gep_knownidx(strct, idxs, sty);
2595+
}
2596+
}
2597+
}
2598+
2599+
if (addr != NULL) {
2600+
int align = jl_datatype_size(fieldty);
2601+
typed_store(addr, ConstantInt::get(T_int32, 0), emit_expr(args[3], ctx),
2602+
(jl_value_t*)fieldty, ctx, tbaa_user, strct, align);
2603+
JL_GC_POP();
2604+
return ghostValue(jl_typeof(jl_nothing));
2605+
}
2606+
}
2607+
}
2608+
}
24912609
else if (f->fptr == &jl_f_nfields && nargs==1) {
24922610
if (ctx->vaStack && symbol_eq(args[1], ctx->vaName) && !ctx->vars[ctx->vaName].isAssigned) {
24932611
JL_GC_POP();
@@ -5059,6 +5177,16 @@ static void init_julia_llvm_env(Module *m)
50595177
jlundefvarerror_func->setDoesNotReturn();
50605178
add_named_global(jlundefvarerror_func, (void*)&jl_undefined_var_error);
50615179

5180+
std::vector<Type*> args2_boundserror(0);
5181+
args2_boundserror.push_back(jl_pvalue_llvmt);
5182+
args2_boundserror.push_back(jl_pvalue_llvmt);
5183+
jlboundserror_func =
5184+
Function::Create(FunctionType::get(T_void, args2_boundserror, false),
5185+
Function::ExternalLinkage,
5186+
"jl_bounds_error", m);
5187+
jlboundserror_func->setDoesNotReturn();
5188+
add_named_global(jlboundserror_func, (void*)&jl_bounds_error);
5189+
50625190
std::vector<Type*> args2_boundserrorv(0);
50635191
args2_boundserrorv.push_back(jl_pvalue_llvmt);
50645192
args2_boundserrorv.push_back(T_psize);
@@ -5070,15 +5198,15 @@ static void init_julia_llvm_env(Module *m)
50705198
jlboundserrorv_func->setDoesNotReturn();
50715199
add_named_global(jlboundserrorv_func, (void*)&jl_bounds_error_ints);
50725200

5073-
std::vector<Type*> args2_boundserror(0);
5074-
args2_boundserror.push_back(jl_pvalue_llvmt);
5075-
args2_boundserror.push_back(T_size);
5076-
jlboundserror_func =
5077-
Function::Create(FunctionType::get(T_void, args2_boundserror, false),
5201+
std::vector<Type*> args2_boundserrorint(0);
5202+
args2_boundserrorint.push_back(jl_pvalue_llvmt);
5203+
args2_boundserrorint.push_back(T_size);
5204+
jlboundserrorint_func =
5205+
Function::Create(FunctionType::get(T_void, args2_boundserrorint, false),
50785206
Function::ExternalLinkage,
50795207
"jl_bounds_error_int", m);
5080-
jlboundserror_func->setDoesNotReturn();
5081-
add_named_global(jlboundserror_func, (void*)&jl_bounds_error_int);
5208+
jlboundserrorint_func->setDoesNotReturn();
5209+
add_named_global(jlboundserrorint_func, (void*)&jl_bounds_error_int);
50825210

50835211
std::vector<Type*> args3_vboundserror(0);
50845212
args3_vboundserror.push_back(jl_ppvalue_llvmt);
@@ -5190,6 +5318,7 @@ static void init_julia_llvm_env(Module *m)
51905318
builtin_func_map[jl_f_isdefined] = jlcall_func_to_llvm("jl_f_isdefined", (void*)&jl_f_isdefined, m);
51915319
builtin_func_map[jl_f_get_field] = jlcall_func_to_llvm("jl_f_get_field", (void*)&jl_f_get_field, m);
51925320
builtin_func_map[jl_f_set_field] = jlcall_func_to_llvm("jl_f_set_field", (void*)&jl_f_set_field, m);
5321+
builtin_func_map[jl_f_modifyelement] = jlcall_func_to_llvm("jl_f_modifyelement", (void*)&jl_f_modifyelement, m);
51935322
builtin_func_map[jl_f_field_type] = jlcall_func_to_llvm("jl_f_field_type", (void*)&jl_f_field_type, m);
51945323
builtin_func_map[jl_f_nfields] = jlcall_func_to_llvm("jl_f_nfields", (void*)&jl_f_nfields, m);
51955324
builtin_func_map[jl_f_new_expr] = jlcall_func_to_llvm("jl_f_new_expr", (void*)&jl_f_new_expr, m);

0 commit comments

Comments
 (0)