Skip to content

Commit

Permalink
Add some tests
Browse files Browse the repository at this point in the history
  • Loading branch information
quinnj committed Jun 22, 2017
1 parent e9d63e0 commit dbc36f1
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 76 deletions.
26 changes: 15 additions & 11 deletions src/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -495,15 +495,20 @@ JL_DLLEXPORT size_t jl_arraymaxsize(jl_array_t *a)
return a->flags.ndims == 1 ? a->maxsize : a->length;
}

JL_DLLEXPORT int jl_ptrarray(jl_array_t *a)
{
return a->flags.ptrarray;
}

JL_DLLEXPORT int jl_elsize(jl_array_t *a)
{
return a->elsize;
}
JL_DLLEXPORT size_t jl_elsize(jl_array_t *a)
{
return a->elsize;
}

JL_DLLEXPORT int jl_ptrarray(jl_array_t *a)
{
return a->flags.ptrarray;
}

JL_DLLEXPORT size_t jl_isunion(jl_array_t *a)
{
return jl_is_uniontype(jl_tparam0(jl_typeof(a)));
}

JL_DLLEXPORT jl_value_t *jl_arrayref(jl_array_t *a, size_t i)
{
Expand All @@ -512,7 +517,6 @@ JL_DLLEXPORT jl_value_t *jl_arrayref(jl_array_t *a, size_t i)
if (!a->flags.ptrarray) {
jl_value_t *eltype = (jl_value_t*)jl_tparam0(jl_typeof(a));
if (jl_is_uniontype(eltype)) {
//TODO
uint8_t sel = ((uint8_t*)a->data)[jl_arraymaxsize(a) * a->elsize + i];
eltype = jl_nth_union_component(eltype, sel);
if (jl_is_datatype_singleton((jl_datatype_t*)eltype))
Expand Down Expand Up @@ -583,7 +587,7 @@ JL_DLLEXPORT void jl_arrayset(jl_array_t *a, jl_value_t *rhs, size_t i)
if (!jl_find_union_component(eltype, jl_typeof(rhs), &nth))
assert(0 && "invalid arrayset to isbits union");
*psel = nth;
if (jl_is_datatype_singleton((jl_datatype_t*)eltype))
if (jl_is_datatype_singleton((jl_datatype_t*)jl_typeof(rhs)))
return;
}
jl_assign_bits(&((char*)a->data)[i * a->elsize], rhs);
Expand Down
136 changes: 71 additions & 65 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2627,34 +2627,34 @@ static bool emit_builtin_call(jl_cgval_t *ret, jl_value_t *f, jl_value_t **args,
jl_value_t *ety = jl_tparam0(aty_dt);
if (!jl_has_free_typevars(ety)) { // TODO: jn/foreigncall branch has a better predicate
size_t elsz, al;
if (!jl_islayout_inline(ety, &elsz, &al))
int isboxed = !jl_islayout_inline(ety, &elsz, &al);
if (isboxed)
ety = (jl_value_t*)jl_any_type;
jl_value_t *ndp = jl_tparam1(aty_dt);
if (jl_is_long(ndp) || nargs==2) {
jl_cgval_t ary = emit_expr(args[1], ctx);
ssize_t nd = jl_is_long(ndp) ? jl_unbox_long(ndp) : -1;
Value *idx = emit_array_nd_index(ary, args[1], nd, &args[2], nargs-1, ctx);
if (jl_array_store_unboxed(ety) &&
jl_datatype_size(ety) == 0) {
assert(jl_is_datatype(ety));
assert(((jl_datatype_t*)ety)->instance != NULL);
*ret = ghostValue(ety);
}
else if (jl_is_uniontype(ety)) {
Type *AT = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), (elsz + al - 1) / al);
AllocaInst *lv = emit_static_alloca(AT, ctx);
if (al > 1)
lv->setAlignment(al);
if (!isboxed && jl_is_uniontype(ety)) {
Value *nbytes = ConstantInt::get(T_size, elsz);
Value *data = emit_arrayptr(ary, args[1], ctx);
Value *elidx = builder.CreateMul(idx, nbytes);
builder.CreateMemCpy(lv, builder.CreateGEP(T_int8, data, elidx), nbytes, al);
Value *selidx = builder.CreateMul(emit_arraymaxsize_prim(ary, ctx), nbytes);
selidx = builder.CreateAdd(selidx, idx);
Value *ptindex = builder.CreateGEP(T_int8, data, selidx);
Value *tindex = builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), builder.CreateLoad(T_int8, ptindex));
Type *AT = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), (elsz + al - 1) / al);
AllocaInst *lv = emit_static_alloca(AT, ctx);
if (al > 1)
lv->setAlignment(al);
Value *elidx = builder.CreateMul(idx, nbytes);
builder.CreateMemCpy(lv, builder.CreateGEP(T_int8, data, elidx), nbytes, al);
*ret = mark_julia_slot(lv, ety, tindex, tbaa_stack);
}
else if (!isboxed && jl_datatype_size(ety) == 0){
assert(jl_is_datatype(ety));
assert(((jl_datatype_t*)ety)->instance != NULL);
*ret = ghostValue(ety);
}
else {
*ret = typed_load(emit_arrayptr(ary, args[1], ctx), idx, ety, ctx,
jl_array_store_unboxed(ety) ? tbaa_arraybuf : tbaa_ptrarraybuf);
Expand Down Expand Up @@ -2689,68 +2689,74 @@ static bool emit_builtin_call(jl_cgval_t *ret, jl_value_t *f, jl_value_t **args,
jl_cgval_t ary = emit_expr(args[1], ctx);
ssize_t nd = jl_is_long(ndp) ? jl_unbox_long(ndp) : -1;
Value *idx = emit_array_nd_index(ary, args[1], nd, &args[3], nargs-2, ctx);
if (!isboxed && jl_datatype_size(ety) == 0) {
jl_cgval_t rhs = emit_expr(args[2], ctx);
PHINode *data_owner = NULL; // owner object against which the write barrier must check
if (isboxed) { // if not boxed we don't need a write barrier
assert(ary.isboxed);
Value *aryv = maybe_decay_untracked(boxed(ary, ctx));
Value *flags = emit_arrayflags(ary, ctx);
// the owner of the data is ary itself except if ary->how == 3
flags = builder.CreateAnd(flags, 3);
Value *is_owned = builder.CreateICmpEQ(flags, ConstantInt::get(T_int16, 3));
BasicBlock *curBB = builder.GetInsertBlock();
BasicBlock *ownedBB = BasicBlock::Create(jl_LLVMContext, "array_owned", ctx->f);
BasicBlock *mergeBB = BasicBlock::Create(jl_LLVMContext, "merge_own", ctx->f);
builder.CreateCondBr(is_owned, ownedBB, mergeBB);
builder.SetInsertPoint(ownedBB);
// load owner pointer
Value *own_ptr;
if (jl_is_long(ndp)) {
own_ptr = tbaa_decorate(tbaa_const, builder.CreateLoad(
emit_bitcast(
builder.CreateConstGEP1_32(
emit_bitcast(decay_derived(aryv), T_pint8),
jl_array_data_owner_offset(nd)),
T_pprjlvalue)));
}
else {
own_ptr = builder.CreateCall(
prepare_call(jlarray_data_owner_func),
{aryv});
}
builder.CreateBr(mergeBB);
builder.SetInsertPoint(mergeBB);
data_owner = builder.CreatePHI(T_prjlvalue, 2);
data_owner->addIncoming(aryv, curBB);
data_owner->addIncoming(own_ptr, ownedBB);
}
else if (jl_datatype_size(ety) == 0) {
// no-op, but emit expr for possible effects
assert(jl_is_datatype(ety));
emit_expr(args[2], ctx);
}
else {
jl_cgval_t rhs = emit_expr(args[2], ctx);
PHINode *data_owner = NULL; // owner object against which the write barrier must check
if (isboxed) { // if not boxed we don't need a write barrier
assert(ary.isboxed);
Value *aryv = maybe_decay_untracked(boxed(ary, ctx));
Value *flags = emit_arrayflags(ary, ctx);
// the owner of the data is ary itself except if ary->how == 3
flags = builder.CreateAnd(flags, 3);
Value *is_owned = builder.CreateICmpEQ(flags, ConstantInt::get(T_int16, 3));
BasicBlock *curBB = builder.GetInsertBlock();
BasicBlock *ownedBB = BasicBlock::Create(jl_LLVMContext, "array_owned", ctx->f);
BasicBlock *mergeBB = BasicBlock::Create(jl_LLVMContext, "merge_own", ctx->f);
builder.CreateCondBr(is_owned, ownedBB, mergeBB);
builder.SetInsertPoint(ownedBB);
// load owner pointer
Value *own_ptr;
if (jl_is_long(ndp)) {
own_ptr = tbaa_decorate(tbaa_const, builder.CreateLoad(
emit_bitcast(
builder.CreateConstGEP1_32(
emit_bitcast(decay_derived(aryv), T_pint8),
jl_array_data_owner_offset(nd)),
T_pprjlvalue)));
}
else {
own_ptr = builder.CreateCall(
prepare_call(jlarray_data_owner_func),
{aryv});
}
builder.CreateBr(mergeBB);
builder.SetInsertPoint(mergeBB);
data_owner = builder.CreatePHI(T_prjlvalue, 2);
data_owner->addIncoming(aryv, curBB);
data_owner->addIncoming(own_ptr, ownedBB);
else if (jl_is_uniontype(ety)) {
Value *nbytes = ConstantInt::get(T_size, elsz);
Value *data = emit_arrayptr(ary, args[1], ctx);
// compute tindex from rhs
jl_cgval_t rhs_union = convert_julia_type(rhs, ety, ctx);
Value *tindex = compute_tindex_unboxed(rhs_union, ety, ctx);
tindex = builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1));
Value *selidx = builder.CreateMul(emit_arraymaxsize_prim(ary, ctx), nbytes);
selidx = builder.CreateAdd(selidx, idx);
Value *ptindex = builder.CreateGEP(T_int8, data, selidx);
builder.CreateStore(tindex, ptindex);
if (jl_datatype_size((jl_datatype_t*)jl_typeof(args[2])) == 0) {
// no-op, but emit expr for possible effects
assert(jl_is_datatype(ety));
emit_expr(args[2], ctx);
}
if (jl_is_uniontype(ety)) {
else {
// copy data
Value *nbytes = ConstantInt::get(T_size, elsz);
Value *data = emit_arrayptr(ary, args[1], ctx);
Value *elidx = builder.CreateMul(idx, nbytes);
Value *addr = builder.CreateGEP(T_int8, data, elidx);
emit_unionmove(addr, rhs, NULL, false, NULL, ctx);
// compute tindex from rhs
jl_cgval_t rhs_union = convert_julia_type(rhs, ety, ctx);
Value *tindex = compute_tindex_unboxed(rhs_union, ety, ctx);
tindex = builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1));
Value *selidx = builder.CreateMul(emit_arraymaxsize_prim(ary, ctx), nbytes);
selidx = builder.CreateAdd(selidx, idx);
Value *ptindex = builder.CreateGEP(T_int8, data, selidx);
builder.CreateStore(tindex, ptindex);
} else {
typed_store(emit_arrayptr(ary, args[1], ctx, isboxed), idx, rhs,
ety, ctx, !isboxed ? tbaa_arraybuf : tbaa_ptrarraybuf, data_owner, 0,
false); // don't need to root the box if we had to make one since it's being stored in the array immediatly
}
}
else {
typed_store(emit_arrayptr(ary, args[1], ctx, isboxed), idx, rhs,
ety, ctx, !isboxed ? tbaa_arraybuf : tbaa_ptrarraybuf, data_owner, 0,
false); // don't need to root the box if we had to make one since it's being stored in the array immediatly
}
*ret = ary;
JL_GC_POP();
return true;
Expand Down
106 changes: 106 additions & 0 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5065,3 +5065,109 @@ f_isdefined_cl_6() = (local x; () -> @isdefined x)
@test !f_isdefined_cl_4()
@test f_isdefined_cl_5()()
@test !f_isdefined_cl_6()()

module UnionOptimizations

const testuniontypes = []
for i = 1:128
@eval struct $(Symbol("TestUnionType$i")); val::Int8; end
@eval push!(testuniontypes, $(Symbol("TestUnionType$i")))
end

const boxedunions = [Union{}, Union{String, Void}, Union{testuniontypes...}]
const unboxedunions = [Union{Int8, Void}, Union{Int8, Float16, Void},
Union{Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128},
Union{Char, Date, Number}]

initvalue(::Type{Void}) = nothing
initvalue(::Type{Char}) = '\0'
initvalue(::Type{Date}) = Date(0, 12, 31)
initvalue(::Type{T}) where {T <: Number} = T(0)

initvalue2(::Type{Void}) = nothing
initvalue2(::Type{Char}) = Char(0x01)
initvalue2(::Type{Date}) = Date(1)
initvalue2(::Type{T}) where {T <: Number} = T(1)


for U in boxedunions
for N in (1, 2, 3, 4)
A = Array{U, N}(0)
@test isempty(A)
@test Core.sizeof(A) == 0

A = Array{U, N}(100)
@test length(A) == 100
@test Core.sizeof(A) == 800
@test !isassigned(A, 1)
end
end

# unsafe_wrap
A4 = [1, 2, 3]
@test_throws ArgumentError unsafe_wrap(Array, convert(Ptr{Union{Int, Void}}, pointer(A4)), 3)
A5 = [1 2 3; 4 5 6]
@test_throws ArgumentError unsafe_wrap(Array, convert(Ptr{Union{Int, Void}}, pointer(A5)), 6)

for U in unboxedunions
for N in (1, 2, 3, 4)
A = Array{U, N}(0)
@test isempty(A)
@test Core.sizeof(A) == 0

len = ntuple(x->10, N)
mxsz = maximum(sizeof, Base.uniontypes(U))
A = Array{U, N}(len)
@test length(A) == prod(len)
@test Core.sizeof(A) == prod(len) * mxsz
@test isassigned(A, 1)
@test isassigned(A, length(A))

# arrayref / arrayset
F = Base.uniontypes(U)[1]
@test A[1] === initvalue(F)
A[1] = initvalue2(F)
@test A[1] === initvalue2(F)

for (i, U2) in enumerate(Base.uniontypes(U))
@test A[i] === initvalue(U2)
A[i] = initvalue2(U2)
@test A[i] === initvalue2(U2)
end

# serialize / deserialize
io = IOBuffer()
serialize(io, v)
seekstart(io)
A2 = deserialize(io)
@test A == A2

# reshape
A3 = reshape(A, (div(prod(len), 2), 2))
@test Core.sizeof(A) == prod(len) * mxsz
@test isassigned(A, 1)
@test A[1] === initvalue(F)

if N == 1
# Dequeue functions
A[end] = initvalue(F)
push!(A, initvalue2(F))
@test A[end] === initvalue2(F)
v = pop!(A)
@test v === initvalue2(F)
append!(A, [initvalue(F), initvalue2(F)])
@test A[end-1] === initvalue(F)
@test A[end] === initvalue2(F)
A[1] = initvalue(F)
unshift!(A, initvalue2(F))
@test A[1] === initvalue2(F)
@test shift!(A) === initvalue2(F)
@test A[1] === initvalue(F)
insert!(A, 5, initvalue2(F))
@test A[5] === initvalue2(F)
deleteat!(A, 5)
end
end
end

end # module

0 comments on commit dbc36f1

Please sign in to comment.