Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,7 @@ enum jl_memory_order jl_get_atomic_order(jl_sym_t *order, char loading, char sto
{
if (order == not_atomic_sym)
return jl_memory_order_notatomic;
if (order == unordered_sym && (loading || storing))
if (order == unordered_sym && (loading ^ storing))
return jl_memory_order_unordered;
if (order == monotonic_sym && (loading || storing))
return jl_memory_order_monotonic;
Expand Down
28 changes: 25 additions & 3 deletions src/cgutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1549,10 +1549,25 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx,
bool needlock, bool issetfield, bool isreplacefield, bool maybe_null_if_boxed)
{
assert(!needlock || parent != nullptr);
jl_cgval_t oldval = rhs;
Type *elty = isboxed ? T_prjlvalue : julia_type_to_llvm(ctx, jltype);
if (type_is_ghost(elty))
return oldval;
if (type_is_ghost(elty)) {
if (isStrongerThanMonotonic(Order))
ctx.builder.CreateFence(Order);
if (issetfield) {
return rhs;
}
else if (isreplacefield) {
Value *Success = emit_f_is(ctx, cmp, ghostValue(jltype));
Success = ctx.builder.CreateZExt(Success, T_int8);
jl_cgval_t argv[2] = {ghostValue(jltype), mark_julia_type(ctx, Success, false, jl_bool_type)};
// TODO: do better here
Value *instr = emit_jlcall(ctx, jltuple_func, V_rnull, argv, 2, JLCALL_F_CC);
return mark_julia_type(ctx, instr, true, jl_any_type);
}
else {
return ghostValue(jltype);
}
}
Value *intcast = nullptr;
if (!isboxed && Order != AtomicOrdering::NotAtomic && !elty->isIntOrPtrTy()) {
const DataLayout &DL = jl_data_layout;
Expand Down Expand Up @@ -1590,6 +1605,7 @@ static jl_cgval_t typed_store(jl_codectx_t &ctx,
BasicBlock *DoneBB = issetfield || (!isreplacefield && !isboxed) ? nullptr : BasicBlock::Create(jl_LLVMContext, "done_xchg", ctx.f);
if (needlock)
emit_lockstate_value(ctx, parent, true);
jl_cgval_t oldval = rhs;
if (issetfield || Order == AtomicOrdering::NotAtomic) {
if (!issetfield) {
instr = ctx.builder.CreateAlignedLoad(elty, ptr, Align(alignment));
Expand Down Expand Up @@ -3230,6 +3246,12 @@ static jl_cgval_t emit_setfield(jl_codectx_t &ctx,
}
if (needlock)
emit_lockstate_value(ctx, strct, false);
if (isreplacefield) {
jl_cgval_t argv[2] = {oldval, mark_julia_type(ctx, Success, false, jl_bool_type)};
// TODO: do better here
Value *instr = emit_jlcall(ctx, jltuple_func, V_rnull, argv, 2, JLCALL_F_CC);
oldval = mark_julia_type(ctx, instr, true, jl_any_type);
}
return oldval;
}
else {
Expand Down
196 changes: 101 additions & 95 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2883,7 +2883,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f,

else if (f == jl_builtin_arrayset && nargs >= 4) {
const jl_cgval_t &ary = argv[2];
const jl_cgval_t &val = argv[3];
jl_cgval_t val = argv[3];
bool indices_ok = true;
for (size_t i = 4; i <= nargs; i++) {
if (argv[i].typ != (jl_value_t*)jl_long_type) {
Expand All @@ -2896,101 +2896,103 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f,
jl_value_t *ety = jl_tparam0(aty_dt);
jl_value_t *ndp = jl_tparam1(aty_dt);
if (!jl_has_free_typevars(ety) && (jl_is_long(ndp) || nargs == 4)) {
if (jl_subtype(val.typ, ety)) { // TODO: probably should just convert this to a type-assert
size_t elsz = 0, al = 0;
int union_max = jl_islayout_inline(ety, &elsz, &al);
bool isboxed = (union_max == 0);
if (isboxed)
ety = (jl_value_t*)jl_any_type;
jl_value_t *ary_ex = jl_exprarg(ex, 2);
ssize_t nd = jl_is_long(ndp) ? jl_unbox_long(ndp) : -1;
jl_value_t *boundscheck = argv[1].constant;
emit_typecheck(ctx, argv[1], (jl_value_t*)jl_bool_type, "arrayset");
Value *idx = emit_array_nd_index(ctx, ary, ary_ex, nd, &argv[4], nargs - 3, boundscheck);
if (!isboxed && jl_is_datatype(ety) && jl_datatype_size(ety) == 0) {
// no-op
}
else {
PHINode *data_owner = NULL; // owner object against which the write barrier must check
if (isboxed || (jl_is_datatype(ety) && ((jl_datatype_t*)ety)->layout->npointers > 0)) { // if elements are just bits, don't need a write barrier
Value *aryv = boxed(ctx, ary);
Value *flags = emit_arrayflags(ctx, ary);
// the owner of the data is ary itself except if ary->how == 3
flags = ctx.builder.CreateAnd(flags, 3);
Value *is_owned = ctx.builder.CreateICmpEQ(flags, ConstantInt::get(T_int16, 3));
BasicBlock *curBB = ctx.builder.GetInsertBlock();
BasicBlock *ownedBB = BasicBlock::Create(jl_LLVMContext, "array_owned", ctx.f);
BasicBlock *mergeBB = BasicBlock::Create(jl_LLVMContext, "merge_own", ctx.f);
ctx.builder.CreateCondBr(is_owned, ownedBB, mergeBB);
ctx.builder.SetInsertPoint(ownedBB);
// load owner pointer
Instruction *own_ptr;
if (jl_is_long(ndp)) {
own_ptr = ctx.builder.CreateAlignedLoad(T_prjlvalue,
ctx.builder.CreateConstInBoundsGEP1_32(T_prjlvalue,
emit_bitcast(ctx, decay_derived(ctx, aryv), T_pprjlvalue),
jl_array_data_owner_offset(nd) / sizeof(jl_value_t*)),
Align(sizeof(void*)));
tbaa_decorate(tbaa_const, maybe_mark_load_dereferenceable(own_ptr, false, (jl_value_t*)jl_array_any_type));
}
else {
own_ptr = ctx.builder.CreateCall(
prepare_call(jlarray_data_owner_func),
{aryv});
}
ctx.builder.CreateBr(mergeBB);
ctx.builder.SetInsertPoint(mergeBB);
data_owner = ctx.builder.CreatePHI(T_prjlvalue, 2);
data_owner->addIncoming(aryv, curBB);
data_owner->addIncoming(own_ptr, ownedBB);
if (!jl_subtype(val.typ, ety)) {
emit_typecheck(ctx, val, ety, "arrayset");
val = update_julia_type(ctx, val, ety);
}
size_t elsz = 0, al = 0;
int union_max = jl_islayout_inline(ety, &elsz, &al);
bool isboxed = (union_max == 0);
if (isboxed)
ety = (jl_value_t*)jl_any_type;
jl_value_t *ary_ex = jl_exprarg(ex, 2);
ssize_t nd = jl_is_long(ndp) ? jl_unbox_long(ndp) : -1;
jl_value_t *boundscheck = argv[1].constant;
emit_typecheck(ctx, argv[1], (jl_value_t*)jl_bool_type, "arrayset");
Value *idx = emit_array_nd_index(ctx, ary, ary_ex, nd, &argv[4], nargs - 3, boundscheck);
if (!isboxed && jl_is_datatype(ety) && jl_datatype_size(ety) == 0) {
// no-op
}
else {
PHINode *data_owner = NULL; // owner object against which the write barrier must check
if (isboxed || (jl_is_datatype(ety) && ((jl_datatype_t*)ety)->layout->npointers > 0)) { // if elements are just bits, don't need a write barrier
Value *aryv = boxed(ctx, ary);
Value *flags = emit_arrayflags(ctx, ary);
// the owner of the data is ary itself except if ary->how == 3
flags = ctx.builder.CreateAnd(flags, 3);
Value *is_owned = ctx.builder.CreateICmpEQ(flags, ConstantInt::get(T_int16, 3));
BasicBlock *curBB = ctx.builder.GetInsertBlock();
BasicBlock *ownedBB = BasicBlock::Create(jl_LLVMContext, "array_owned", ctx.f);
BasicBlock *mergeBB = BasicBlock::Create(jl_LLVMContext, "merge_own", ctx.f);
ctx.builder.CreateCondBr(is_owned, ownedBB, mergeBB);
ctx.builder.SetInsertPoint(ownedBB);
// load owner pointer
Instruction *own_ptr;
if (jl_is_long(ndp)) {
own_ptr = ctx.builder.CreateAlignedLoad(T_prjlvalue,
ctx.builder.CreateConstInBoundsGEP1_32(T_prjlvalue,
emit_bitcast(ctx, decay_derived(ctx, aryv), T_pprjlvalue),
jl_array_data_owner_offset(nd) / sizeof(jl_value_t*)),
Align(sizeof(void*)));
tbaa_decorate(tbaa_const, maybe_mark_load_dereferenceable(own_ptr, false, (jl_value_t*)jl_array_any_type));
}
if (!isboxed && jl_is_uniontype(ety)) {
Type *AT = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), (elsz + al - 1) / al);
Value *data = emit_bitcast(ctx, emit_arrayptr(ctx, ary, ary_ex), AT->getPointerTo());
// compute tindex from val
jl_cgval_t rhs_union = convert_julia_type(ctx, val, ety);
Value *tindex = compute_tindex_unboxed(ctx, rhs_union, ety);
tindex = ctx.builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1));
Value *ndims = (nd == -1 ? emit_arrayndims(ctx, ary) : ConstantInt::get(T_int16, nd));
Value *is_vector = ctx.builder.CreateICmpEQ(ndims, ConstantInt::get(T_int16, 1));
Value *offset = emit_arrayoffset(ctx, ary, nd);
Value *selidx_v = ctx.builder.CreateSub(emit_vectormaxsize(ctx, ary), ctx.builder.CreateZExt(offset, T_size));
Value *selidx_m = emit_arraylen(ctx, ary);
Value *selidx = ctx.builder.CreateSelect(is_vector, selidx_v, selidx_m);
Value *ptindex = ctx.builder.CreateInBoundsGEP(AT, data, selidx);
ptindex = emit_bitcast(ctx, ptindex, T_pint8);
ptindex = ctx.builder.CreateInBoundsGEP(T_int8, ptindex, offset);
ptindex = ctx.builder.CreateInBoundsGEP(T_int8, ptindex, idx);
tbaa_decorate(tbaa_arrayselbyte, ctx.builder.CreateStore(tindex, ptindex));
if (jl_is_datatype(val.typ) && jl_datatype_size(val.typ) == 0) {
// no-op
}
else {
// copy data
Value *addr = ctx.builder.CreateInBoundsGEP(AT, data, idx);
emit_unionmove(ctx, addr, tbaa_arraybuf, val, nullptr);
}
else {
own_ptr = ctx.builder.CreateCall(
prepare_call(jlarray_data_owner_func),
{aryv});
}
ctx.builder.CreateBr(mergeBB);
ctx.builder.SetInsertPoint(mergeBB);
data_owner = ctx.builder.CreatePHI(T_prjlvalue, 2);
data_owner->addIncoming(aryv, curBB);
data_owner->addIncoming(own_ptr, ownedBB);
}
if (!isboxed && jl_is_uniontype(ety)) {
Type *AT = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), (elsz + al - 1) / al);
Value *data = emit_bitcast(ctx, emit_arrayptr(ctx, ary, ary_ex), AT->getPointerTo());
// compute tindex from val
jl_cgval_t rhs_union = convert_julia_type(ctx, val, ety);
Value *tindex = compute_tindex_unboxed(ctx, rhs_union, ety);
tindex = ctx.builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1));
Value *ndims = (nd == -1 ? emit_arrayndims(ctx, ary) : ConstantInt::get(T_int16, nd));
Value *is_vector = ctx.builder.CreateICmpEQ(ndims, ConstantInt::get(T_int16, 1));
Value *offset = emit_arrayoffset(ctx, ary, nd);
Value *selidx_v = ctx.builder.CreateSub(emit_vectormaxsize(ctx, ary), ctx.builder.CreateZExt(offset, T_size));
Value *selidx_m = emit_arraylen(ctx, ary);
Value *selidx = ctx.builder.CreateSelect(is_vector, selidx_v, selidx_m);
Value *ptindex = ctx.builder.CreateInBoundsGEP(AT, data, selidx);
ptindex = emit_bitcast(ctx, ptindex, T_pint8);
ptindex = ctx.builder.CreateInBoundsGEP(T_int8, ptindex, offset);
ptindex = ctx.builder.CreateInBoundsGEP(T_int8, ptindex, idx);
tbaa_decorate(tbaa_arrayselbyte, ctx.builder.CreateStore(tindex, ptindex));
if (jl_is_datatype(val.typ) && jl_datatype_size(val.typ) == 0) {
// no-op
}
else {
typed_store(ctx,
emit_arrayptr(ctx, ary, ary_ex, isboxed),
idx, val, jl_cgval_t(), ety,
isboxed ? tbaa_ptrarraybuf : tbaa_arraybuf,
ctx.aliasscope,
data_owner,
isboxed,
isboxed ? AtomicOrdering::Unordered : AtomicOrdering::NotAtomic, // TODO: we should do this for anything with CountTrackedPointers(elty).count > 0
isboxed ? AtomicOrdering::Unordered : AtomicOrdering::NotAtomic, // TODO: we should do this for anything with CountTrackedPointers(elty).count > 0
0,
false,
true,
false,
false);
// copy data
Value *addr = ctx.builder.CreateInBoundsGEP(AT, data, idx);
emit_unionmove(ctx, addr, tbaa_arraybuf, val, nullptr);
}
}
*ret = ary;
return true;
else {
typed_store(ctx,
emit_arrayptr(ctx, ary, ary_ex, isboxed),
idx, val, jl_cgval_t(), ety,
isboxed ? tbaa_ptrarraybuf : tbaa_arraybuf,
ctx.aliasscope,
data_owner,
isboxed,
isboxed ? AtomicOrdering::Unordered : AtomicOrdering::NotAtomic, // TODO: we should do this for anything with CountTrackedPointers(elty).count > 0
isboxed ? AtomicOrdering::Unordered : AtomicOrdering::NotAtomic, // TODO: we should do this for anything with CountTrackedPointers(elty).count > 0
0,
false,
true,
false,
false);
}
}
*ret = ary;
return true;
}
}
}
Expand Down Expand Up @@ -3134,21 +3136,21 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f,
const jl_cgval_t undefval;
const jl_cgval_t &obj = argv[1];
const jl_cgval_t &fld = argv[2];
const jl_cgval_t &val = argv[isreplacefield ? 4 : 3];
jl_cgval_t val = argv[isreplacefield ? 4 : 3];
const jl_cgval_t &cmp = isreplacefield ? argv[3] : undefval;
enum jl_memory_order order = jl_memory_order_notatomic;
const std::string fname = issetfield ? "setfield!" : isreplacefield ? "replacefield!" : "swapfield!";
if (nargs >= (isreplacefield ? 5 : 4)) {
const jl_cgval_t &ord = argv[isreplacefield ? 5 : 4];
emit_typecheck(ctx, ord, (jl_value_t*)jl_symbol_type,
issetfield ? "setfield!" : isreplacefield ? "replacefield!" : "swapfield!");
emit_typecheck(ctx, ord, (jl_value_t*)jl_symbol_type, fname);
if (!ord.constant)
return false;
order = jl_get_atomic_order((jl_sym_t*)ord.constant, !issetfield, true);
}
enum jl_memory_order fail_order = order;
if (isreplacefield && nargs == 6) {
const jl_cgval_t &ord = argv[6];
emit_typecheck(ctx, ord, (jl_value_t*)jl_symbol_type, "replacefield!");
emit_typecheck(ctx, ord, (jl_value_t*)jl_symbol_type, fname);
if (!ord.constant)
return false;
fail_order = jl_get_atomic_order((jl_sym_t*)ord.constant, true, false);
Expand All @@ -3172,7 +3174,11 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f,
}
if (idx != -1) {
jl_value_t *ft = jl_svecref(uty->types, idx);
if (!jl_has_free_typevars(ft) && jl_subtype(val.typ, ft)) {
if (!jl_has_free_typevars(ft)) {
if (!jl_subtype(val.typ, ft)) {
emit_typecheck(ctx, val, ft, fname);
val = update_julia_type(ctx, val, ft);
}
// TODO: attempt better codegen for approximate types
bool isboxed = jl_field_isptr(uty, idx);
bool isatomic = jl_field_isatomic(uty, idx);
Expand Down
Loading