Skip to content

Commit dbc36f1

Browse files
committed
Add some tests
1 parent e9d63e0 commit dbc36f1

File tree

3 files changed

+192
-76
lines changed

3 files changed

+192
-76
lines changed

src/array.c

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -495,15 +495,20 @@ JL_DLLEXPORT size_t jl_arraymaxsize(jl_array_t *a)
495495
return a->flags.ndims == 1 ? a->maxsize : a->length;
496496
}
497497

498-
JL_DLLEXPORT int jl_ptrarray(jl_array_t *a)
499-
{
500-
return a->flags.ptrarray;
501-
}
502-
503-
JL_DLLEXPORT int jl_elsize(jl_array_t *a)
504-
{
505-
return a->elsize;
506-
}
498+
JL_DLLEXPORT size_t jl_elsize(jl_array_t *a)
499+
{
500+
return a->elsize;
501+
}
502+
503+
JL_DLLEXPORT int jl_ptrarray(jl_array_t *a)
504+
{
505+
return a->flags.ptrarray;
506+
}
507+
508+
JL_DLLEXPORT size_t jl_isunion(jl_array_t *a)
509+
{
510+
return jl_is_uniontype(jl_tparam0(jl_typeof(a)));
511+
}
507512

508513
JL_DLLEXPORT jl_value_t *jl_arrayref(jl_array_t *a, size_t i)
509514
{
@@ -512,7 +517,6 @@ JL_DLLEXPORT jl_value_t *jl_arrayref(jl_array_t *a, size_t i)
512517
if (!a->flags.ptrarray) {
513518
jl_value_t *eltype = (jl_value_t*)jl_tparam0(jl_typeof(a));
514519
if (jl_is_uniontype(eltype)) {
515-
//TODO
516520
uint8_t sel = ((uint8_t*)a->data)[jl_arraymaxsize(a) * a->elsize + i];
517521
eltype = jl_nth_union_component(eltype, sel);
518522
if (jl_is_datatype_singleton((jl_datatype_t*)eltype))
@@ -583,7 +587,7 @@ JL_DLLEXPORT void jl_arrayset(jl_array_t *a, jl_value_t *rhs, size_t i)
583587
if (!jl_find_union_component(eltype, jl_typeof(rhs), &nth))
584588
assert(0 && "invalid arrayset to isbits union");
585589
*psel = nth;
586-
if (jl_is_datatype_singleton((jl_datatype_t*)eltype))
590+
if (jl_is_datatype_singleton((jl_datatype_t*)jl_typeof(rhs)))
587591
return;
588592
}
589593
jl_assign_bits(&((char*)a->data)[i * a->elsize], rhs);

src/codegen.cpp

Lines changed: 71 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2627,34 +2627,34 @@ static bool emit_builtin_call(jl_cgval_t *ret, jl_value_t *f, jl_value_t **args,
26272627
jl_value_t *ety = jl_tparam0(aty_dt);
26282628
if (!jl_has_free_typevars(ety)) { // TODO: jn/foreigncall branch has a better predicate
26292629
size_t elsz, al;
2630-
if (!jl_islayout_inline(ety, &elsz, &al))
2630+
int isboxed = !jl_islayout_inline(ety, &elsz, &al);
2631+
if (isboxed)
26312632
ety = (jl_value_t*)jl_any_type;
26322633
jl_value_t *ndp = jl_tparam1(aty_dt);
26332634
if (jl_is_long(ndp) || nargs==2) {
26342635
jl_cgval_t ary = emit_expr(args[1], ctx);
26352636
ssize_t nd = jl_is_long(ndp) ? jl_unbox_long(ndp) : -1;
26362637
Value *idx = emit_array_nd_index(ary, args[1], nd, &args[2], nargs-1, ctx);
2637-
if (jl_array_store_unboxed(ety) &&
2638-
jl_datatype_size(ety) == 0) {
2639-
assert(jl_is_datatype(ety));
2640-
assert(((jl_datatype_t*)ety)->instance != NULL);
2641-
*ret = ghostValue(ety);
2642-
}
2643-
else if (jl_is_uniontype(ety)) {
2644-
Type *AT = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), (elsz + al - 1) / al);
2645-
AllocaInst *lv = emit_static_alloca(AT, ctx);
2646-
if (al > 1)
2647-
lv->setAlignment(al);
2638+
if (!isboxed && jl_is_uniontype(ety)) {
26482639
Value *nbytes = ConstantInt::get(T_size, elsz);
26492640
Value *data = emit_arrayptr(ary, args[1], ctx);
2650-
Value *elidx = builder.CreateMul(idx, nbytes);
2651-
builder.CreateMemCpy(lv, builder.CreateGEP(T_int8, data, elidx), nbytes, al);
26522641
Value *selidx = builder.CreateMul(emit_arraymaxsize_prim(ary, ctx), nbytes);
26532642
selidx = builder.CreateAdd(selidx, idx);
26542643
Value *ptindex = builder.CreateGEP(T_int8, data, selidx);
26552644
Value *tindex = builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), builder.CreateLoad(T_int8, ptindex));
2645+
Type *AT = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), (elsz + al - 1) / al);
2646+
AllocaInst *lv = emit_static_alloca(AT, ctx);
2647+
if (al > 1)
2648+
lv->setAlignment(al);
2649+
Value *elidx = builder.CreateMul(idx, nbytes);
2650+
builder.CreateMemCpy(lv, builder.CreateGEP(T_int8, data, elidx), nbytes, al);
26562651
*ret = mark_julia_slot(lv, ety, tindex, tbaa_stack);
26572652
}
2653+
else if (!isboxed && jl_datatype_size(ety) == 0){
2654+
assert(jl_is_datatype(ety));
2655+
assert(((jl_datatype_t*)ety)->instance != NULL);
2656+
*ret = ghostValue(ety);
2657+
}
26582658
else {
26592659
*ret = typed_load(emit_arrayptr(ary, args[1], ctx), idx, ety, ctx,
26602660
jl_array_store_unboxed(ety) ? tbaa_arraybuf : tbaa_ptrarraybuf);
@@ -2689,68 +2689,74 @@ static bool emit_builtin_call(jl_cgval_t *ret, jl_value_t *f, jl_value_t **args,
26892689
jl_cgval_t ary = emit_expr(args[1], ctx);
26902690
ssize_t nd = jl_is_long(ndp) ? jl_unbox_long(ndp) : -1;
26912691
Value *idx = emit_array_nd_index(ary, args[1], nd, &args[3], nargs-2, ctx);
2692-
if (!isboxed && jl_datatype_size(ety) == 0) {
2692+
jl_cgval_t rhs = emit_expr(args[2], ctx);
2693+
PHINode *data_owner = NULL; // owner object against which the write barrier must check
2694+
if (isboxed) { // if not boxed we don't need a write barrier
2695+
assert(ary.isboxed);
2696+
Value *aryv = maybe_decay_untracked(boxed(ary, ctx));
2697+
Value *flags = emit_arrayflags(ary, ctx);
2698+
// the owner of the data is ary itself except if ary->how == 3
2699+
flags = builder.CreateAnd(flags, 3);
2700+
Value *is_owned = builder.CreateICmpEQ(flags, ConstantInt::get(T_int16, 3));
2701+
BasicBlock *curBB = builder.GetInsertBlock();
2702+
BasicBlock *ownedBB = BasicBlock::Create(jl_LLVMContext, "array_owned", ctx->f);
2703+
BasicBlock *mergeBB = BasicBlock::Create(jl_LLVMContext, "merge_own", ctx->f);
2704+
builder.CreateCondBr(is_owned, ownedBB, mergeBB);
2705+
builder.SetInsertPoint(ownedBB);
2706+
// load owner pointer
2707+
Value *own_ptr;
2708+
if (jl_is_long(ndp)) {
2709+
own_ptr = tbaa_decorate(tbaa_const, builder.CreateLoad(
2710+
emit_bitcast(
2711+
builder.CreateConstGEP1_32(
2712+
emit_bitcast(decay_derived(aryv), T_pint8),
2713+
jl_array_data_owner_offset(nd)),
2714+
T_pprjlvalue)));
2715+
}
2716+
else {
2717+
own_ptr = builder.CreateCall(
2718+
prepare_call(jlarray_data_owner_func),
2719+
{aryv});
2720+
}
2721+
builder.CreateBr(mergeBB);
2722+
builder.SetInsertPoint(mergeBB);
2723+
data_owner = builder.CreatePHI(T_prjlvalue, 2);
2724+
data_owner->addIncoming(aryv, curBB);
2725+
data_owner->addIncoming(own_ptr, ownedBB);
2726+
}
2727+
else if (jl_datatype_size(ety) == 0) {
26932728
// no-op, but emit expr for possible effects
26942729
assert(jl_is_datatype(ety));
26952730
emit_expr(args[2], ctx);
26962731
}
2697-
else {
2698-
jl_cgval_t rhs = emit_expr(args[2], ctx);
2699-
PHINode *data_owner = NULL; // owner object against which the write barrier must check
2700-
if (isboxed) { // if not boxed we don't need a write barrier
2701-
assert(ary.isboxed);
2702-
Value *aryv = maybe_decay_untracked(boxed(ary, ctx));
2703-
Value *flags = emit_arrayflags(ary, ctx);
2704-
// the owner of the data is ary itself except if ary->how == 3
2705-
flags = builder.CreateAnd(flags, 3);
2706-
Value *is_owned = builder.CreateICmpEQ(flags, ConstantInt::get(T_int16, 3));
2707-
BasicBlock *curBB = builder.GetInsertBlock();
2708-
BasicBlock *ownedBB = BasicBlock::Create(jl_LLVMContext, "array_owned", ctx->f);
2709-
BasicBlock *mergeBB = BasicBlock::Create(jl_LLVMContext, "merge_own", ctx->f);
2710-
builder.CreateCondBr(is_owned, ownedBB, mergeBB);
2711-
builder.SetInsertPoint(ownedBB);
2712-
// load owner pointer
2713-
Value *own_ptr;
2714-
if (jl_is_long(ndp)) {
2715-
own_ptr = tbaa_decorate(tbaa_const, builder.CreateLoad(
2716-
emit_bitcast(
2717-
builder.CreateConstGEP1_32(
2718-
emit_bitcast(decay_derived(aryv), T_pint8),
2719-
jl_array_data_owner_offset(nd)),
2720-
T_pprjlvalue)));
2721-
}
2722-
else {
2723-
own_ptr = builder.CreateCall(
2724-
prepare_call(jlarray_data_owner_func),
2725-
{aryv});
2726-
}
2727-
builder.CreateBr(mergeBB);
2728-
builder.SetInsertPoint(mergeBB);
2729-
data_owner = builder.CreatePHI(T_prjlvalue, 2);
2730-
data_owner->addIncoming(aryv, curBB);
2731-
data_owner->addIncoming(own_ptr, ownedBB);
2732+
else if (jl_is_uniontype(ety)) {
2733+
Value *nbytes = ConstantInt::get(T_size, elsz);
2734+
Value *data = emit_arrayptr(ary, args[1], ctx);
2735+
// compute tindex from rhs
2736+
jl_cgval_t rhs_union = convert_julia_type(rhs, ety, ctx);
2737+
Value *tindex = compute_tindex_unboxed(rhs_union, ety, ctx);
2738+
tindex = builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1));
2739+
Value *selidx = builder.CreateMul(emit_arraymaxsize_prim(ary, ctx), nbytes);
2740+
selidx = builder.CreateAdd(selidx, idx);
2741+
Value *ptindex = builder.CreateGEP(T_int8, data, selidx);
2742+
builder.CreateStore(tindex, ptindex);
2743+
if (jl_datatype_size((jl_datatype_t*)jl_typeof(args[2])) == 0) {
2744+
// no-op, but emit expr for possible effects
2745+
assert(jl_is_datatype(ety));
2746+
emit_expr(args[2], ctx);
27322747
}
2733-
if (jl_is_uniontype(ety)) {
2748+
else {
27342749
// copy data
2735-
Value *nbytes = ConstantInt::get(T_size, elsz);
2736-
Value *data = emit_arrayptr(ary, args[1], ctx);
27372750
Value *elidx = builder.CreateMul(idx, nbytes);
27382751
Value *addr = builder.CreateGEP(T_int8, data, elidx);
27392752
emit_unionmove(addr, rhs, NULL, false, NULL, ctx);
2740-
// compute tindex from rhs
2741-
jl_cgval_t rhs_union = convert_julia_type(rhs, ety, ctx);
2742-
Value *tindex = compute_tindex_unboxed(rhs_union, ety, ctx);
2743-
tindex = builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1));
2744-
Value *selidx = builder.CreateMul(emit_arraymaxsize_prim(ary, ctx), nbytes);
2745-
selidx = builder.CreateAdd(selidx, idx);
2746-
Value *ptindex = builder.CreateGEP(T_int8, data, selidx);
2747-
builder.CreateStore(tindex, ptindex);
2748-
} else {
2749-
typed_store(emit_arrayptr(ary, args[1], ctx, isboxed), idx, rhs,
2750-
ety, ctx, !isboxed ? tbaa_arraybuf : tbaa_ptrarraybuf, data_owner, 0,
2751-
false); // don't need to root the box if we had to make one since it's being stored in the array immediatly
27522753
}
27532754
}
2755+
else {
2756+
typed_store(emit_arrayptr(ary, args[1], ctx, isboxed), idx, rhs,
2757+
ety, ctx, !isboxed ? tbaa_arraybuf : tbaa_ptrarraybuf, data_owner, 0,
2758+
false); // don't need to root the box if we had to make one since it's being stored in the array immediatly
2759+
}
27542760
*ret = ary;
27552761
JL_GC_POP();
27562762
return true;

test/core.jl

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5065,3 +5065,109 @@ f_isdefined_cl_6() = (local x; () -> @isdefined x)
50655065
@test !f_isdefined_cl_4()
50665066
@test f_isdefined_cl_5()()
50675067
@test !f_isdefined_cl_6()()
5068+
5069+
module UnionOptimizations
5070+
5071+
const testuniontypes = []
5072+
for i = 1:128
5073+
@eval struct $(Symbol("TestUnionType$i")); val::Int8; end
5074+
@eval push!(testuniontypes, $(Symbol("TestUnionType$i")))
5075+
end
5076+
5077+
const boxedunions = [Union{}, Union{String, Void}, Union{testuniontypes...}]
5078+
const unboxedunions = [Union{Int8, Void}, Union{Int8, Float16, Void},
5079+
Union{Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128},
5080+
Union{Char, Date, Number}]
5081+
5082+
initvalue(::Type{Void}) = nothing
5083+
initvalue(::Type{Char}) = '\0'
5084+
initvalue(::Type{Date}) = Date(0, 12, 31)
5085+
initvalue(::Type{T}) where {T <: Number} = T(0)
5086+
5087+
initvalue2(::Type{Void}) = nothing
5088+
initvalue2(::Type{Char}) = Char(0x01)
5089+
initvalue2(::Type{Date}) = Date(1)
5090+
initvalue2(::Type{T}) where {T <: Number} = T(1)
5091+
5092+
5093+
for U in boxedunions
5094+
for N in (1, 2, 3, 4)
5095+
A = Array{U, N}(0)
5096+
@test isempty(A)
5097+
@test Core.sizeof(A) == 0
5098+
5099+
A = Array{U, N}(100)
5100+
@test length(A) == 100
5101+
@test Core.sizeof(A) == 800
5102+
@test !isassigned(A, 1)
5103+
end
5104+
end
5105+
5106+
# unsafe_wrap
5107+
A4 = [1, 2, 3]
5108+
@test_throws ArgumentError unsafe_wrap(Array, convert(Ptr{Union{Int, Void}}, pointer(A4)), 3)
5109+
A5 = [1 2 3; 4 5 6]
5110+
@test_throws ArgumentError unsafe_wrap(Array, convert(Ptr{Union{Int, Void}}, pointer(A5)), 6)
5111+
5112+
for U in unboxedunions
5113+
for N in (1, 2, 3, 4)
5114+
A = Array{U, N}(0)
5115+
@test isempty(A)
5116+
@test Core.sizeof(A) == 0
5117+
5118+
len = ntuple(x->10, N)
5119+
mxsz = maximum(sizeof, Base.uniontypes(U))
5120+
A = Array{U, N}(len)
5121+
@test length(A) == prod(len)
5122+
@test Core.sizeof(A) == prod(len) * mxsz
5123+
@test isassigned(A, 1)
5124+
@test isassigned(A, length(A))
5125+
5126+
# arrayref / arrayset
5127+
F = Base.uniontypes(U)[1]
5128+
@test A[1] === initvalue(F)
5129+
A[1] = initvalue2(F)
5130+
@test A[1] === initvalue2(F)
5131+
5132+
for (i, U2) in enumerate(Base.uniontypes(U))
5133+
@test A[i] === initvalue(U2)
5134+
A[i] = initvalue2(U2)
5135+
@test A[i] === initvalue2(U2)
5136+
end
5137+
5138+
# serialize / deserialize
5139+
io = IOBuffer()
5140+
serialize(io, v)
5141+
seekstart(io)
5142+
A2 = deserialize(io)
5143+
@test A == A2
5144+
5145+
# reshape
5146+
A3 = reshape(A, (div(prod(len), 2), 2))
5147+
@test Core.sizeof(A) == prod(len) * mxsz
5148+
@test isassigned(A, 1)
5149+
@test A[1] === initvalue(F)
5150+
5151+
if N == 1
5152+
# Dequeue functions
5153+
A[end] = initvalue(F)
5154+
push!(A, initvalue2(F))
5155+
@test A[end] === initvalue2(F)
5156+
v = pop!(A)
5157+
@test v === initvalue2(F)
5158+
append!(A, [initvalue(F), initvalue2(F)])
5159+
@test A[end-1] === initvalue(F)
5160+
@test A[end] === initvalue2(F)
5161+
A[1] = initvalue(F)
5162+
unshift!(A, initvalue2(F))
5163+
@test A[1] === initvalue2(F)
5164+
@test shift!(A) === initvalue2(F)
5165+
@test A[1] === initvalue(F)
5166+
insert!(A, 5, initvalue2(F))
5167+
@test A[5] === initvalue2(F)
5168+
deleteat!(A, 5)
5169+
end
5170+
end
5171+
end
5172+
5173+
end # module

0 commit comments

Comments
 (0)