-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
make memorynew
intrinsic
#55913
base: master
Are you sure you want to change the base?
make memorynew
intrinsic
#55913
Changes from all commits
53616ec
81755e6
cb79529
7b6d78e
e3d60b6
4b9c7b3
a9b4fae
933770b
c18b684
86a7ba9
21be744
3d614f0
80c58b6
5d1051a
4094ae9
38f262c
2ff0656
7e32bef
516a702
8631d2a
9dcaf13
311f5ef
59d0d2f
db95887
94f569a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -1643,6 +1643,14 @@ JL_CALLABLE(jl_f__typevar) | |||||||
} | ||||||||
|
||||||||
// genericmemory --------------------------------------------------------------------- | ||||||||
JL_CALLABLE(jl_f_memorynew) | ||||||||
{ | ||||||||
JL_NARGS(memorynew, 2, 2); | ||||||||
JL_TYPECHK(memorynew, datatype, args[0]); | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should probably also check that it is a GenericMemory subtype
Suggested change
|
||||||||
JL_TYPECHK(memorynew, long, args[1]); | ||||||||
size_t nel = jl_unbox_long(args[1]); | ||||||||
return (jl_value_t*)jl_alloc_genericmemory(args[0], nel); | ||||||||
} | ||||||||
|
||||||||
JL_CALLABLE(jl_f_memoryref) | ||||||||
{ | ||||||||
|
@@ -2409,6 +2417,7 @@ void jl_init_primitives(void) JL_GC_DISABLED | |||||||
jl_builtin_setglobalonce = add_builtin_func("setglobalonce!", jl_f_setglobalonce); | ||||||||
|
||||||||
// memory primitives | ||||||||
jl_builtin_memorynew = add_builtin_func("memorynew", jl_f_memorynew); | ||||||||
jl_builtin_memoryref = add_builtin_func("memoryrefnew", jl_f_memoryref); | ||||||||
jl_builtin_memoryrefoffset = add_builtin_func("memoryrefoffset", jl_f_memoryrefoffset); | ||||||||
jl_builtin_memoryrefget = add_builtin_func("memoryrefget", jl_f_memoryrefget); | ||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1602,19 +1602,24 @@ static void emit_error(jl_codectx_t &ctx, const Twine &txt) | |
} | ||
|
||
// DO NOT PASS IN A CONST CONDITION! | ||
static void error_unless(jl_codectx_t &ctx, Value *cond, const Twine &msg) | ||
static void error_unless(jl_codectx_t &ctx, Function *F, Value *cond, const Twine &msg) | ||
{ | ||
++EmittedConditionalErrors; | ||
BasicBlock *failBB = BasicBlock::Create(ctx.builder.getContext(), "fail", ctx.f); | ||
BasicBlock *passBB = BasicBlock::Create(ctx.builder.getContext(), "pass"); | ||
ctx.builder.CreateCondBr(cond, passBB, failBB); | ||
ctx.builder.SetInsertPoint(failBB); | ||
just_emit_error(ctx, prepare_call(jlerror_func), msg); | ||
just_emit_error(ctx, F, msg); | ||
ctx.builder.CreateUnreachable(); | ||
passBB->insertInto(ctx.f); | ||
ctx.builder.SetInsertPoint(passBB); | ||
} | ||
|
||
static void error_unless(jl_codectx_t &ctx, Value *cond, const Twine &msg) | ||
{ | ||
error_unless(ctx, prepare_call(jlerror_func), cond, msg); | ||
} | ||
|
||
static void raise_exception(jl_codectx_t &ctx, Value *exc, | ||
BasicBlock *contBB=nullptr) | ||
{ | ||
|
@@ -4490,6 +4495,189 @@ static int compare_cgparams(const jl_cgparams_t *a, const jl_cgparams_t *b) | |
} | ||
#endif | ||
|
||
|
||
static jl_cgval_t emit_const_len_memorynew(jl_codectx_t &ctx, jl_datatype_t *typ, size_t nel, jl_genericmemory_t *inst) | ||
{ | ||
if (nel == 0){ | ||
Value *empty_alloc = track_pjlvalue(ctx, literal_pointer_val(ctx, (jl_value_t*)inst)); | ||
return mark_julia_type(ctx, empty_alloc, true, typ); | ||
} | ||
const jl_datatype_layout_t *layout = ((jl_datatype_t*)typ)->layout; | ||
assert(((jl_datatype_t*)typ)->has_concrete_subtype && layout != NULL); | ||
size_t elsz = layout->size; | ||
int isboxed = layout->flags.arrayelem_isboxed; | ||
int isunion = layout->flags.arrayelem_isunion; | ||
int zi = ((jl_datatype_t*)typ)->zeroinit; | ||
if (isboxed) | ||
elsz = sizeof(void*); | ||
|
||
size_t nbytes; | ||
bool overflow = __builtin_mul_overflow(nel, elsz, &nbytes); | ||
if (isunion) { | ||
// an extra byte for each isbits union memory element, stored at m->ptr + m->length | ||
overflow |= __builtin_add_overflow(nbytes, nel, &nbytes); | ||
} | ||
Comment on lines
+4514
to
+4519
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This arithmetic appears to be unsigned, while the overflow itself is supposed to be a signed check (or multiply elsz by 2 for the same effect) also may be missing the final |
||
if (overflow) | ||
emit_error(ctx, prepare_call(jlargumenterror_func), "invalid GenericMemory size: the number of elements is either negative or too large for system address width"); | ||
|
||
auto ct = get_current_task(ctx); | ||
auto T_size = ctx.types().T_size; | ||
auto int8t = getInt8Ty(ctx.builder.getContext()); | ||
auto cg_typ = literal_pointer_val(ctx, (jl_value_t*) typ); | ||
auto cg_nbytes = ConstantInt::get(T_size, nbytes); | ||
auto cg_nel = ConstantInt::get(T_size, nel); | ||
|
||
// else actually allocate mem | ||
auto arg_typename = [&] JL_NOTSAFEPOINT { | ||
std::string type_str; | ||
auto eltype = jl_tparam1(typ); | ||
if (jl_is_datatype(eltype)) | ||
type_str = jl_symbol_name(((jl_datatype_t*)eltype)->name->name); | ||
else if (jl_is_uniontype(eltype)) | ||
type_str = "Union"; | ||
else | ||
type_str = "<unknown type>"; | ||
return "Memory{" + type_str + "}[]"; | ||
}; | ||
size_t tot = nbytes + LLT_ALIGN(sizeof(jl_genericmemory_t),JL_SMALL_BYTE_ALIGNMENT); | ||
|
||
int pooled = tot <= GC_MAX_SZCLASS; | ||
Value *alloc, *decay_alloc, *memory_ptr; | ||
jl_aliasinfo_t aliasinfo; | ||
if (pooled) { | ||
auto cg_tot = ConstantInt::get(T_size, tot); | ||
auto call = prepare_call(jl_alloc_obj_func); | ||
alloc = ctx.builder.CreateCall(call, { ct, cg_tot, track_pjlvalue(ctx, cg_typ)}); | ||
decay_alloc = decay_derived(ctx, alloc); | ||
memory_ptr = ctx.builder.CreateStructGEP(ctx.types().T_jlgenericmemory, decay_alloc, 1); | ||
setName(ctx.emission_context, memory_ptr, "memory_ptr"); | ||
auto objref = emit_pointer_from_objref(ctx, alloc); | ||
Value *memory_data = emit_ptrgep(ctx, objref, JL_SMALL_BYTE_ALIGNMENT); | ||
auto *store = ctx.builder.CreateAlignedStore(memory_data, memory_ptr, Align(sizeof(void*))); | ||
aliasinfo = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_memoryptr); | ||
aliasinfo.decorateInst(store); | ||
setName(ctx.emission_context, memory_data, "memory_data"); | ||
} else { // just use the dynamic length version since the malloc will be slow anyway | ||
auto ptls = get_current_ptls(ctx); | ||
auto call = prepare_call(jl_alloc_genericmemory_unchecked_func); | ||
alloc = ctx.builder.CreateCall(call, { ptls, cg_nbytes, cg_typ}); | ||
decay_alloc = maybe_decay_tracked(ctx, alloc); | ||
} | ||
// set length (jl_alloc_genericmemory_unchecked_func doesn't have it) | ||
Value *len_field = ctx.builder.CreateStructGEP(ctx.types().T_jlgenericmemory, decay_alloc, 0); | ||
auto *len_store = ctx.builder.CreateAlignedStore(cg_nel, len_field, Align(sizeof(void*))); | ||
aliasinfo = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_memorylen); | ||
aliasinfo.decorateInst(len_store); | ||
|
||
// zeroinit pointers and unions | ||
if (zi) { | ||
memory_ptr = ctx.builder.CreateStructGEP(ctx.types().T_jlgenericmemory, decay_alloc, 1); | ||
auto *load = ctx.builder.CreateAlignedLoad(ctx.types().T_ptr, memory_ptr, Align(sizeof(void*))); | ||
aliasinfo = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_memoryptr); | ||
aliasinfo.decorateInst(load); | ||
ctx.builder.CreateMemSet(load, ConstantInt::get(int8t, 0), cg_nbytes, Align(sizeof(void*))); | ||
} | ||
|
||
setName(ctx.emission_context, alloc, arg_typename); | ||
return mark_julia_type(ctx, alloc, true, typ); | ||
} | ||
|
||
static jl_cgval_t emit_memorynew(jl_codectx_t &ctx, jl_datatype_t *typ, jl_cgval_t nel, jl_genericmemory_t *inst) | ||
{ | ||
emit_typecheck(ctx, nel, (jl_value_t*)jl_long_type, "memorynew"); | ||
nel = update_julia_type(ctx, nel, (jl_value_t*)jl_long_type); | ||
if (nel.typ == jl_bottom_type) | ||
return jl_cgval_t(); | ||
|
||
const jl_datatype_layout_t *layout = ((jl_datatype_t*)typ)->layout; | ||
assert(((jl_datatype_t*)typ)->has_concrete_subtype && layout != NULL); | ||
size_t elsz = layout->size; | ||
int isboxed = layout->flags.arrayelem_isboxed; | ||
int isunion = layout->flags.arrayelem_isunion; | ||
int zi = ((jl_datatype_t*)typ)->zeroinit; | ||
if (isboxed) | ||
elsz = sizeof(void*); | ||
|
||
auto ptls = get_current_ptls(ctx); | ||
auto T_size = ctx.types().T_size; | ||
auto int8t = getInt8Ty(ctx.builder.getContext()); | ||
BasicBlock *emptymemBB, *nonemptymemBB, *retvalBB; | ||
emptymemBB = BasicBlock::Create(ctx.builder.getContext(), "emptymem"); | ||
nonemptymemBB = BasicBlock::Create(ctx.builder.getContext(), "nonemptymem"); | ||
retvalBB = BasicBlock::Create(ctx.builder.getContext(), "retval"); | ||
auto nel_unboxed = emit_unbox(ctx, ctx.types().T_size, nel, (jl_value_t*)jl_long_type); | ||
Value *memorynew_empty = ctx.builder.CreateICmpEQ(nel_unboxed, ConstantInt::get(T_size, 0)); | ||
setName(ctx.emission_context, memorynew_empty, "memorynew_empty"); | ||
ctx.builder.CreateCondBr(memorynew_empty, emptymemBB, nonemptymemBB); | ||
// if nel == 0 | ||
emptymemBB->insertInto(ctx.f); | ||
ctx.builder.SetInsertPoint(emptymemBB); | ||
auto emptyalloc = track_pjlvalue(ctx, literal_pointer_val(ctx, (jl_value_t*)inst)); | ||
ctx.builder.CreateBr(retvalBB); | ||
nonemptymemBB->insertInto(ctx.f); | ||
ctx.builder.SetInsertPoint(nonemptymemBB); | ||
// else actually allocate mem | ||
auto arg_typename = [&] JL_NOTSAFEPOINT { | ||
std::string type_str; | ||
auto eltype = jl_tparam1(typ); | ||
if (jl_is_datatype(eltype)) | ||
type_str = jl_symbol_name(((jl_datatype_t*)eltype)->name->name); | ||
else if (jl_is_uniontype(eltype)) | ||
type_str = "Union"; | ||
else | ||
type_str = "<unknown type>"; | ||
return "Memory{" + type_str + "}[]"; | ||
}; | ||
auto cg_typ = literal_pointer_val(ctx, (jl_value_t*) typ); | ||
auto cg_elsz = ConstantInt::get(T_size, elsz); | ||
|
||
FunctionCallee intr = Intrinsic::getDeclaration(jl_Module, Intrinsic::smul_with_overflow, ArrayRef<Type*>(T_size)); | ||
// compute nbytes with possible overflow | ||
Value *prod_with_overflow = ctx.builder.CreateCall(intr, {nel_unboxed, cg_elsz}); | ||
Value *nbytes = ctx.builder.CreateExtractValue(prod_with_overflow, 0); | ||
Value *overflow = ctx.builder.CreateExtractValue(prod_with_overflow, 1); | ||
if (isunion) { | ||
// if isunion, we need to allocate the union selector bytes as well | ||
intr = Intrinsic::getDeclaration(jl_Module, Intrinsic::sadd_with_overflow, ArrayRef<Type*>(T_size)); | ||
Value *add_with_overflow = ctx.builder.CreateCall(intr, {nel_unboxed, nbytes}); | ||
nbytes = ctx.builder.CreateExtractValue(add_with_overflow, 0); | ||
Value *overflow1 = ctx.builder.CreateExtractValue(add_with_overflow, 1); | ||
overflow = ctx.builder.CreateOr(overflow, overflow1); | ||
} | ||
Value *negnel = ctx.builder.CreateICmpSLT(nel_unboxed, ConstantInt::get(T_size, 0)); | ||
overflow = ctx.builder.CreateOr(overflow, negnel); | ||
Value *notoverflow = ctx.builder.CreateNot(overflow); | ||
error_unless(ctx, prepare_call(jlargumenterror_func), notoverflow, "invalid GenericMemory size: the number of elements is either negative or too large for system address width"); | ||
// actually allocate | ||
auto call = prepare_call(jl_alloc_genericmemory_unchecked_func); | ||
Value *alloc = ctx.builder.CreateCall(call, { ptls, nbytes, cg_typ}); | ||
// set length (jl_alloc_genericmemory_unchecked_func doesn't have it) | ||
Value *decay_alloc = decay_derived(ctx, alloc); | ||
Value *len_field = ctx.builder.CreateStructGEP(ctx.types().T_jlgenericmemory, decay_alloc, 0); | ||
auto len_store = ctx.builder.CreateAlignedStore(nel_unboxed, len_field, Align(sizeof(void*))); | ||
auto aliasinfo = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_memorylen); | ||
aliasinfo.decorateInst(len_store); | ||
// zeroinit pointers and unions | ||
if (zi) { | ||
Value *memory_ptr = ctx.builder.CreateStructGEP(ctx.types().T_jlgenericmemory, decay_alloc, 1); | ||
auto *load = ctx.builder.CreateAlignedLoad(ctx.types().T_ptr, memory_ptr, Align(sizeof(void*))); | ||
aliasinfo = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_memoryptr); | ||
aliasinfo.decorateInst(load); | ||
ctx.builder.CreateMemSet(load, ConstantInt::get(int8t, 0), nbytes, Align(sizeof(void*))); | ||
} | ||
|
||
setName(ctx.emission_context, alloc, arg_typename); | ||
ctx.builder.CreateBr(retvalBB); | ||
nonemptymemBB = ctx.builder.GetInsertBlock(); | ||
// phi node to choose which side of branch | ||
retvalBB->insertInto(ctx.f); | ||
ctx.builder.SetInsertPoint(retvalBB); | ||
auto phi = ctx.builder.CreatePHI(ctx.types().T_prjlvalue, 2); | ||
phi->addIncoming(emptyalloc, emptymemBB); | ||
phi->addIncoming(alloc, nonemptymemBB); | ||
return mark_julia_type(ctx, phi, true, typ); | ||
} | ||
|
||
static jl_cgval_t _emit_memoryref(jl_codectx_t &ctx, Value *mem, Value *data, const jl_datatype_layout_t *layout, jl_value_t *typ) | ||
{ | ||
//jl_cgval_t argv[] = { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we just make this the bootstrap method too? The boundscheck macro is just expanded simply to
@_boundscheck &&
, so this seems compatibleThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds reasonable