Skip to content

Commit

Permalink
Improve code generation for homogenous tuples of unknown length
Browse files Browse the repository at this point in the history
E.g.:
```
mutable struct foo
    x::Int
end
mutable struct bar
    tup::NTuple{N, foo} where N
end
function f(b::bar)
    sum = 0
    for i = 1:2
        @inbounds sum += b.tup[i].x
    end
    sum
end
global x = bar((foo(1),foo(2)))
@benchmark f(x::bar)
```

Before:
```
julia> @benchmark f(x::bar)
BenchmarkTools.Trial:
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     27.124 ns (0.00% GC)
  median time:      28.771 ns (0.00% GC)
  mean time:        29.864 ns (0.00% GC)
  maximum time:     116.762 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     995
```

After:
```
BenchmarkTools.Trial:
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     10.531 ns (0.00% GC)
  median time:      10.559 ns (0.00% GC)
  mean time:        10.610 ns (0.00% GC)
  maximum time:     122.751 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     999
  time tolerance:   5.00%
  memory tolerance: 1.00%
```
  • Loading branch information
Keno committed Apr 22, 2017
1 parent dc6c583 commit a761f10
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 11 deletions.
37 changes: 27 additions & 10 deletions src/cgutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -544,15 +544,24 @@ static bool is_datatype_all_pointers(jl_datatype_t *dt)
return true;
}

static bool is_tupletype_homogeneous(jl_svec_t *t)
static bool is_tupletype_homogeneous(jl_svec_t *t, bool allow_va = false)
{
size_t i, l = jl_svec_len(t);
if (l > 0) {
jl_value_t *t0 = jl_svecref(t, 0);
if (!jl_is_leaf_type(t0))
if (!jl_is_leaf_type(t0)) {
if (allow_va && jl_is_vararg_type(t0) &&
jl_is_leaf_type(jl_unwrap_vararg(t0)))
return true;
return false;
}
for(i=1; i < l; i++) {
if (!jl_types_equal(t0, jl_svecref(t,i)))
if (allow_va && i == l - 1 && jl_is_vararg_type(jl_svecref(t,i))) {
if (t0 != jl_unwrap_vararg(jl_svecref(t,i)))
return false;
continue;
}
if (t0 != jl_svecref(t,i))
return false;
}
}
Expand Down Expand Up @@ -1080,13 +1089,21 @@ static void emit_leafcheck(Value *typ, const std::string &msg, jl_codectx_t *ctx
}

#define CHECK_BOUNDS 1
static bool bounds_check_enabled(jl_codectx_t *ctx) {
#if CHECK_BOUNDS==1
return (!ctx->is_inbounds &&
jl_options.check_bounds != JL_OPTIONS_CHECK_BOUNDS_OFF) ||
jl_options.check_bounds == JL_OPTIONS_CHECK_BOUNDS_ON;
#else
return 0;
#endif
}

static Value *emit_bounds_check(const jl_cgval_t &ainfo, jl_value_t *ty, Value *i, Value *len, jl_codectx_t *ctx)
{
Value *im1 = builder.CreateSub(i, ConstantInt::get(T_size, 1));
#if CHECK_BOUNDS==1
if ((!ctx->is_inbounds &&
jl_options.check_bounds != JL_OPTIONS_CHECK_BOUNDS_OFF) ||
jl_options.check_bounds == JL_OPTIONS_CHECK_BOUNDS_ON) {
if (bounds_check_enabled(ctx)) {
Value *ok = builder.CreateICmpULT(im1, len);
BasicBlock *failBB = BasicBlock::Create(jl_LLVMContext,"fail",ctx->f);
BasicBlock *passBB = BasicBlock::Create(jl_LLVMContext,"pass");
Expand Down Expand Up @@ -1157,7 +1174,7 @@ static unsigned julia_alignment(Value* /*ptr*/, jl_value_t *jltype, unsigned ali
static Value *emit_unbox(Type *to, const jl_cgval_t &x, jl_value_t *jt, Value* dest = NULL, bool volatile_store = false);

static jl_cgval_t typed_load(Value *ptr, Value *idx_0based, jl_value_t *jltype,
jl_codectx_t *ctx, MDNode *tbaa, unsigned alignment = 0)
jl_codectx_t *ctx, MDNode *tbaa, bool maybe_null_if_boxed = true, unsigned alignment = 0)
{
bool isboxed;
Type *elty = julia_type_to_llvm(jltype, &isboxed);
Expand Down Expand Up @@ -1187,7 +1204,7 @@ static jl_cgval_t typed_load(Value *ptr, Value *idx_0based, jl_value_t *jltype,
else {
elt = load;
}
if (isboxed) {
if (maybe_null_if_boxed && isboxed) {
null_pointer_check(elt, ctx);
}
//}
Expand Down Expand Up @@ -1361,7 +1378,7 @@ static bool emit_getfield_unknownidx(jl_cgval_t *ret, const jl_cgval_t &strct,
ret->isimmutable = strct.isimmutable;
return true;
}
*ret = typed_load(ptr, idx, jt, ctx, strct.tbaa);
*ret = typed_load(ptr, idx, jt, ctx, strct.tbaa, false);
return true;
}
else if (strct.isboxed) {
Expand Down Expand Up @@ -1458,7 +1475,7 @@ static jl_cgval_t emit_getfield_knownidx(const jl_cgval_t &strct, unsigned idx,
int align = jl_field_offset(jt, idx);
align |= 16;
align &= -align;
return typed_load(addr, ConstantInt::get(T_size, 0), jfty, ctx, strct.tbaa, align);
return typed_load(addr, ConstantInt::get(T_size, 0), jfty, ctx, strct.tbaa, true, align);
}
else if (isa<UndefValue>(strct.V)) {
return jl_cgval_t();
Expand Down
27 changes: 27 additions & 0 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3010,6 +3010,33 @@ static bool emit_builtin_call(jl_cgval_t *ret, jl_value_t *f, jl_value_t **args,
}
}
}
} else {
jl_value_t *utt = jl_unwrap_unionall((jl_value_t*)stt);
if (jl_is_tuple_type(utt) && is_tupletype_homogeneous(((jl_datatype_t*)utt)->types, true)) {
// For tuples, we can emit code even if we don't know the exact
// type (e.g. because we don't know the length). So long as all
// elements are the same and a leaf type.
jl_cgval_t strct = emit_expr(args[1], ctx);
if (strct.ispointer()) {
// Determine which was the type that was homogenous
jl_value_t *jt = jl_tparam0(utt);
if (jl_is_vararg_type(jt))
jt = jl_unwrap_vararg(jt);
Value *vidx = emit_unbox(T_size, emit_expr(args[2], ctx), (jl_value_t*)jl_long_type);
// This is not necessary for correctness, but allows to omit
// the extra code for getting the length of the tuple
if (!bounds_check_enabled(ctx)) {
vidx = builder.CreateSub(vidx, ConstantInt::get(T_size, 1));
} else {
vidx = emit_bounds_check(strct, (jl_value_t*)stt, vidx,
emit_datatype_nfields(emit_typeof_boxed(strct, ctx)), ctx);
}
Value *ptr = data_pointer(strct, ctx);
*ret = typed_load(ptr, vidx, jt, ctx, strct.tbaa, false);
JL_GC_POP();
return true;
}
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/intrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ static jl_cgval_t emit_pointerref(jl_cgval_t *argv, jl_codectx_t *ctx)
Type *ptrty = julia_type_to_llvm(e.typ, &isboxed);
assert(!isboxed);
Value *thePtr = emit_unbox(ptrty, e, e.typ);
return typed_load(thePtr, im1, ety, ctx, tbaa_data, align_nb);
return typed_load(thePtr, im1, ety, ctx, tbaa_data, true, align_nb);
}

static jl_cgval_t emit_runtime_pointerset(jl_cgval_t *argv, jl_codectx_t *ctx)
Expand Down

0 comments on commit a761f10

Please sign in to comment.