// run time version of pointerref intrinsic (warning: i is not rooted) JL_DLLEXPORT jl_value_t *jl_pointerref(jl_value_t *p, jl_value_t *i) { JL_TYPECHK(pointerref, pointer, p); JL_TYPECHK(pointerref, long, i); jl_value_t *ety = jl_tparam0(jl_typeof(p)); if (ety == (jl_value_t*)jl_any_type) { jl_value_t **pp = (jl_value_t**)(jl_unbox_long(p) + (jl_unbox_long(i)-1)*sizeof(void*)); return *pp; } else { if (!jl_is_datatype(ety)) jl_error("pointerref: invalid pointer"); size_t nb = LLT_ALIGN(jl_datatype_size(ety), ((jl_datatype_t*)ety)->alignment); char *pp = (char*)jl_unbox_long(p) + (jl_unbox_long(i)-1)*nb; return jl_new_bits(ety, pp); } }
static union jl_typemap_t *mtcache_hash_bp(struct jl_ordereddict_t *pa, jl_value_t *ty, int8_t tparam, int8_t offs, jl_value_t *parent) { if (jl_is_datatype(ty)) { uintptr_t uid = ((jl_datatype_t*)ty)->uid; if (!uid || is_kind(ty) || jl_has_typevars(ty)) // be careful not to put non-leaf types or DataType/TypeConstructor in the cache here, // since they should have a lower priority and need to go into the sorted list return NULL; if (pa->values == (void*)jl_nothing) { pa->indexes = jl_alloc_int_1d(0, INIT_CACHE_SIZE); jl_gc_wb(parent, pa->indexes); pa->values = jl_alloc_vec_any(0); jl_gc_wb(parent, pa->values); } while (1) { size_t slot = uid & (pa->indexes->nrows - 1); size_t idx = jl_intref(pa->indexes, slot); if (idx == 0) { jl_array_ptr_1d_push(pa->values, jl_nothing); idx = jl_array_len(pa->values); if (idx > jl_max_int(pa->indexes)) mtcache_rehash(pa, jl_array_len(pa->indexes), parent, tparam, offs); jl_intset(pa->indexes, slot, idx); return &((union jl_typemap_t*)jl_array_data(pa->values))[idx - 1]; } union jl_typemap_t *pml = &((union jl_typemap_t*)jl_array_data(pa->values))[idx - 1]; if (pml->unknown == jl_nothing) return pml; jl_value_t *t; if (jl_typeof(pml->unknown) == (jl_value_t*)jl_typemap_level_type) { t = pml->node->key; } else { assert(jl_typeof(pml->unknown) == (jl_value_t*)jl_typemap_entry_type); t = jl_field_type(pml->leaf->sig, offs); if (tparam) t = jl_tparam0(t); } if (t == ty) return pml; mtcache_rehash(pa, jl_array_len(pa->indexes) * 2, parent, tparam, offs); } } return NULL; }
static void jl_typemap_level_insert_(jl_typemap_level_t *cache, jl_typemap_entry_t *newrec, int8_t offs, const struct jl_typemap_info *tparams) { if (jl_datatype_nfields(newrec->sig) > offs) { jl_value_t *t1 = jl_tparam(newrec->sig, offs); // if t1 != jl_typetype_type and the argument is Type{...}, this // method has specializations for singleton kinds and we use // the table indexed for that purpose. if (t1 != (jl_value_t*)jl_typetype_type && jl_is_type_type(t1)) { jl_value_t *a0 = jl_tparam0(t1); if (jl_typemap_array_insert_(&cache->targ, a0, newrec, (jl_value_t*)cache, 1, offs, tparams)) return; } if (jl_typemap_array_insert_(&cache->arg1, t1, newrec, (jl_value_t*)cache, 0, offs, tparams)) return; } jl_typemap_list_insert_(&cache->linear, (jl_value_t*)cache, newrec, tparams); }
static void show_type(jl_value_t *t) { ios_t *s = jl_current_output_stream(); if (jl_is_func_type(t)) { if (t == (jl_value_t*)jl_any_func) { ios_puts("Function", s); } else { jl_show((jl_value_t*)((jl_func_type_t*)t)->from); ios_write(s, "-->", 3); jl_show((jl_value_t*)((jl_func_type_t*)t)->to); } } else if (t == (jl_value_t*)jl_function_type) { ios_puts("Function", s); } else if (jl_is_union_type(t)) { if (t == (jl_value_t*)jl_bottom_type) { ios_write(s, "None", 4); } else if (t == jl_top_type) { ios_write(s, "Top", 3); } else { ios_write(s, "Union", 5); show_tuple(((jl_uniontype_t*)t)->types, '(', ')', 0); } } else if (jl_is_seq_type(t)) { jl_show(jl_tparam0(t)); ios_write(s, "...", 3); } else if (jl_is_typector(t)) { jl_show((jl_value_t*)((jl_typector_t*)t)->body); } else { assert(jl_is_some_tag_type(t)); jl_tag_type_t *tt = (jl_tag_type_t*)t; ios_puts(tt->name->name->name, s); jl_tuple_t *p = tt->parameters; if (p->length > 0) show_tuple(p, '{', '}', 0); } }
// run time version of pointerset intrinsic DLLEXPORT void jl_pointerset(jl_value_t *p, jl_value_t *x, jl_value_t *i) { JL_TYPECHK(pointerset, pointer, p); JL_TYPECHK(pointerset, long, i); jl_value_t *ety = jl_tparam0(jl_typeof(p)); if (ety == (jl_value_t*)jl_any_type) { jl_value_t **pp = (jl_value_t**)(jl_unbox_long(p) + (jl_unbox_long(i)-1)*sizeof(void*)); *pp = x; } else { if (!jl_is_datatype(ety)) jl_error("pointerset: invalid pointer"); size_t nb = LLT_ALIGN(jl_datatype_size(ety), ((jl_datatype_t*)ety)->alignment); char *pp = (char*)jl_unbox_long(p) + (jl_unbox_long(i)-1)*nb; if (jl_typeof(x) != ety) jl_error("pointerset: type mismatch in assign"); jl_assign_bits(pp, x); } }
static jl_function_t *jl_method_cache_insert(jl_methtable_t *mt, jl_tuple_t *type, jl_function_t *method) { jl_methlist_t **pml = &mt->cache; if (type->length > 0) { jl_value_t *t0 = jl_t0(type); uptrint_t uid=0; // if t0 != jl_typetype_type and the argument is Type{...}, this // method has specializations for singleton kinds and we use // the table indexed for that purpose. if (t0 != (jl_value_t*)jl_typetype_type && jl_is_type_type(t0)) { jl_value_t *a0 = jl_tparam0(t0); if (jl_is_struct_type(a0)) uid = ((jl_struct_type_t*)a0)->uid; else if (jl_is_bits_type(a0)) uid = ((jl_bits_type_t*)a0)->uid; if (uid > 0) { if (mt->cache_targ == NULL) mt->cache_targ = jl_alloc_cell_1d(0); if (uid >= jl_array_len(mt->cache_targ)) { jl_array_grow_end(mt->cache_targ, uid+4-jl_array_len(mt->cache_targ)); } pml = (jl_methlist_t**)&jl_cellref(mt->cache_targ, uid); goto ml_do_insert; } } if (jl_is_struct_type(t0)) uid = ((jl_struct_type_t*)t0)->uid; else if (jl_is_bits_type(t0)) uid = ((jl_bits_type_t*)t0)->uid; if (uid > 0) { if (mt->cache_arg1 == NULL) mt->cache_arg1 = jl_alloc_cell_1d(0); if (uid >= jl_array_len(mt->cache_arg1)) { jl_array_grow_end(mt->cache_arg1, uid+4-jl_array_len(mt->cache_arg1)); } pml = (jl_methlist_t**)&jl_cellref(mt->cache_arg1, uid); } } ml_do_insert: return jl_method_list_insert(pml, type, method, jl_null, 0)->func; }
// own_buffer != 0 iff GC should call free() on this pointer eventually JL_DLLEXPORT jl_array_t *jl_ptr_to_array_1d(jl_value_t *atype, void *data, size_t nel, int own_buffer) { jl_ptls_t ptls = jl_get_ptls_states(); size_t elsz; jl_array_t *a; jl_value_t *el_type = jl_tparam0(atype); int isunboxed = store_unboxed(el_type); if (isunboxed) elsz = jl_datatype_size(el_type); else elsz = sizeof(void*); int ndimwords = jl_array_ndimwords(1); int tsz = JL_ARRAY_ALIGN(sizeof(jl_array_t) + ndimwords*sizeof(size_t), JL_CACHE_BYTE_ALIGNMENT); a = (jl_array_t*)jl_gc_alloc(ptls, tsz, atype); // No allocation or safepoint allowed after this a->flags.pooled = tsz <= GC_MAX_SZCLASS; a->data = data; #ifdef STORE_ARRAY_LEN a->length = nel; #endif a->elsize = elsz; a->flags.ptrarray = !isunboxed; a->flags.ndims = 1; a->flags.isshared = 1; a->flags.isaligned = 0; // TODO: allow passing memalign'd buffers if (own_buffer) { a->flags.how = 2; jl_gc_track_malloced_array(ptls, a); jl_gc_count_allocd(nel*elsz + (elsz == 1 ? 1 : 0)); } else { a->flags.how = 0; } a->nrows = nel; a->maxsize = nel; a->offset = 0; return a; }
Type *preferred_llvm_type(jl_datatype_t *dt, bool isret) const override { // Arguments are either scalar or passed by value size_t size = jl_datatype_size(dt); // don't need to change bitstypes if (!jl_datatype_nfields(dt)) return NULL; // legalize this into [n x f32/f64] jl_datatype_t *ty0 = NULL; bool hva = false; int hfa = isHFA(dt, &ty0, &hva); if (hfa <= 8) { if (ty0 == jl_float32_type) { return ArrayType::get(T_float32, hfa); } else if (ty0 == jl_float64_type) { return ArrayType::get(T_float64, hfa); } else { jl_datatype_t *vecty = (jl_datatype_t*)jl_field_type(ty0, 0); assert(jl_is_datatype(vecty) && vecty->name == jl_vecelement_typename); jl_value_t *elemty = jl_tparam0(vecty); assert(jl_is_primitivetype(elemty)); Type *ety = julia_type_to_llvm(elemty); Type *vty = VectorType::get(ety, jl_datatype_nfields(ty0)); return ArrayType::get(vty, hfa); } } // rewrite integer-sized (non-HFA) struct to an array // the bitsize of the integer gives the desired alignment if (size > 8) { if (jl_datatype_align(dt) <= 8) { return ArrayType::get(T_int64, (size + 7) / 8); } else { Type *T_int128 = Type::getIntNTy(jl_LLVMContext, 128); return ArrayType::get(T_int128, (size + 15) / 16); } } return Type::getIntNTy(jl_LLVMContext, size * 8); }
void jl_arrayset(jl_array_t *a, jl_value_t *rhs, size_t i) { assert(i < jl_array_len(a)); jl_value_t *el_type = jl_tparam0(jl_typeof(a)); if (el_type != (jl_value_t*)jl_any_type) { if (!jl_subtype(rhs, el_type, 1)) jl_type_error("arrayset", el_type, rhs); } if (!a->ptrarray) { jl_assign_bits(&((char*)a->data)[i*a->elsize], rhs); } else { ((jl_value_t**)a->data)[i] = rhs; jl_value_t *owner = (jl_value_t*)a; if (a->how == 3) { owner = jl_array_data_owner(a); } jl_gc_wb(owner, rhs); } }
// Note that this function updates len static jl_value_t *jl_new_bits_internal(jl_value_t *dt, void *data, size_t *len) { if (jl_is_ntuple_type(dt)) { jl_value_t *lenvar = jl_tparam0(dt); jl_value_t *elty = jl_tparam1(dt); assert(jl_is_datatype(elty)); size_t alignment = ((jl_datatype_t*)elty)->alignment; *len = LLT_ALIGN((*len), alignment); assert(jl_is_long(lenvar)); size_t l = jl_unbox_long(lenvar); size_t nb = l*LLT_ALIGN(jl_datatype_size(elty), alignment); jl_value_t *v = (jl_value_t*)newobj(dt, NWORDS(nb)); memcpy(jl_data_ptr(v), data, nb); return v; } assert(jl_is_datatype(dt)); jl_datatype_t *bt = (jl_datatype_t*)dt; size_t nb = jl_datatype_size(bt); if (nb == 0) return jl_new_struct_uninit(bt); *len = LLT_ALIGN(*len, bt->alignment); data = (char*)data + (*len); *len += nb; if (bt == jl_uint8_type) return jl_box_uint8(*(uint8_t*)data); if (bt == jl_int64_type) return jl_box_int64(*(int64_t*)data); if (bt == jl_bool_type) return (*(int8_t*)data) ? jl_true:jl_false; if (bt == jl_int32_type) return jl_box_int32(*(int32_t*)data); if (bt == jl_float64_type) return jl_box_float64(*(double*)data); jl_value_t *v = (jl_value_t*)newobj((jl_value_t*)bt, NWORDS(nb)); switch (nb) { case 1: *(int8_t*) jl_data_ptr(v) = *(int8_t*)data; break; case 2: *(int16_t*) jl_data_ptr(v) = *(int16_t*)data; break; case 4: *(int32_t*) jl_data_ptr(v) = *(int32_t*)data; break; case 8: *(int64_t*) jl_data_ptr(v) = *(int64_t*)data; break; case 16: *(bits128_t*)jl_data_ptr(v) = *(bits128_t*)data; break; default: memcpy(jl_data_ptr(v), data, nb); } return v; }
// own_buffer != 0 iff GC should call free() on this pointer eventually jl_array_t *jl_ptr_to_array_1d(jl_value_t *atype, void *data, size_t nel, int own_buffer) { size_t elsz; jl_array_t *a; jl_value_t *el_type = jl_tparam0(atype); int isunboxed = store_unboxed(el_type); if (isunboxed) elsz = jl_datatype_size(el_type); else elsz = sizeof(void*); int ndimwords = jl_array_ndimwords(1); int tsz = JL_ARRAY_ALIGN(sizeof(jl_array_t) + ndimwords*sizeof(size_t), 16); a = (jl_array_t*)jl_gc_allocobj(tsz); jl_set_typeof(a, atype); a->pooled = tsz <= GC_MAX_SZCLASS; a->data = data; #ifdef STORE_ARRAY_LEN a->length = nel; #endif a->elsize = elsz; a->ptrarray = !isunboxed; a->ndims = 1; a->isshared = 1; a->isaligned = 0; // TODO: allow passing memalign'd buffers if (own_buffer) { a->how = 2; jl_gc_track_malloced_array(a); jl_gc_count_allocd(nel*elsz + (elsz == 1 ? 1 : 0)); } else { a->how = 0; } a->nrows = nel; a->maxsize = nel; a->offset = 0; return a; }
static void jl_typemap_level_insert_(jl_typemap_level_t *cache, jl_typemap_entry_t *newrec, int8_t offs, const struct jl_typemap_info *tparams) { jl_value_t *ttypes = jl_unwrap_unionall((jl_value_t*)newrec->sig); size_t l = jl_field_count(ttypes); // compute the type at offset `offs` into `sig`, which may be a Vararg jl_value_t *t1 = NULL; int isva = 0; if (l <= offs + 1) { t1 = jl_tparam(ttypes, l - 1); if (jl_is_vararg_type(t1)) { isva = 1; t1 = jl_unwrap_vararg(t1); } else if (l <= offs) { t1 = NULL; } } else if (l > offs) { t1 = jl_tparam(ttypes, offs); } // If the type at `offs` is Any, put it in the Any list if (t1 && jl_is_any(t1)) { jl_typemap_insert_generic(&cache->any, (jl_value_t*)cache, newrec, (jl_value_t*)jl_any_type, offs+1, tparams); return; } // Don't put Varargs in the optimized caches (too hard to handle in lookup and bp) if (t1 && !isva) { // if t1 != jl_typetype_type and the argument is Type{...}, this // method has specializations for singleton kinds and we use // the table indexed for that purpose. if (t1 != (jl_value_t*)jl_typetype_type && jl_is_type_type(t1)) { jl_value_t *a0 = jl_tparam0(t1); if (jl_typemap_array_insert_(&cache->targ, a0, newrec, (jl_value_t*)cache, 1, offs, tparams)) return; } if (jl_typemap_array_insert_(&cache->arg1, t1, newrec, (jl_value_t*)cache, 0, offs, tparams)) return; } jl_typemap_list_insert_(&cache->linear, (jl_value_t*)cache, newrec, tparams); }
// this is the general entry point for looking up a type in the cache // (as a subtype, or with typeseq) jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_tupletype_t *types, jl_svec_t **penv, int8_t subtype_inexact__sigseq_useenv, int8_t subtype, int8_t offs) { jl_typemap_entry_t *ml; if (jl_typeof(ml_or_cache.unknown) == (jl_value_t*)jl_typemap_level_type) { jl_typemap_level_t *cache = ml_or_cache.node; // called object is the primary key for constructors, otherwise first argument if (jl_datatype_nfields(types) > offs) { jl_value_t *ty = jl_tparam(types, offs); if (cache->targ != (void*)jl_nothing && jl_is_type_type(ty)) { jl_value_t *a0 = jl_tparam0(ty); if (jl_is_datatype(a0)) { union jl_typemap_t ml = mtcache_hash_lookup(cache->targ, a0, 1, offs); if (ml.unknown != jl_nothing) { jl_typemap_entry_t *li = jl_typemap_assoc_by_type(ml, types, penv, subtype_inexact__sigseq_useenv, subtype, offs+1); if (li) return li; } } } if (cache->arg1 != (void*)jl_nothing && jl_is_datatype(ty)) { union jl_typemap_t ml = mtcache_hash_lookup(cache->arg1, ty, 0, offs); if (ml.unknown != jl_nothing) { jl_typemap_entry_t *li = jl_typemap_assoc_by_type(ml, types, penv, subtype_inexact__sigseq_useenv, subtype, offs+1); if (li) return li; } } } ml = cache->linear; } else { ml = ml_or_cache.leaf; } return subtype ? jl_typemap_assoc_by_type_(ml, types, subtype_inexact__sigseq_useenv, penv) : jl_typemap_lookup_by_type_(ml, types, subtype_inexact__sigseq_useenv); }
/* Method caches are divided into three parts: one for signatures where the first argument is a singleton kind (Type{Foo}), one indexed by the UID of the first argument's type in normal cases, and a fallback table of everything else. */ static jl_function_t *jl_method_table_assoc_exact_by_type(jl_methtable_t *mt, jl_tuple_t *types) { jl_methlist_t *ml = NULL; if (types->length > 0) { jl_value_t *ty = jl_t0(types); uptrint_t uid; if (jl_is_type_type(ty)) { jl_value_t *a0 = jl_tparam0(ty); jl_value_t *tty = (jl_value_t*)jl_typeof(a0); if ((tty == (jl_value_t*)jl_struct_kind && (uid = ((jl_struct_type_t*)a0)->uid)) || (tty == (jl_value_t*)jl_bits_kind && (uid = ((jl_bits_type_t*)a0)->uid))) { if (mt->cache_targ && uid < jl_array_len(mt->cache_targ)) { ml = (jl_methlist_t*)jl_cellref(mt->cache_targ, uid); if (ml) goto mt_assoc_bt_lkup; } } } if ((jl_is_struct_type(ty) && (uid = ((jl_struct_type_t*)ty)->uid)) || (jl_is_bits_type(ty) && (uid = ((jl_bits_type_t*)ty)->uid))) { if (mt->cache_arg1 && uid < jl_array_len(mt->cache_arg1)) { ml = (jl_methlist_t*)jl_cellref(mt->cache_arg1, uid); } } } if (ml == NULL) ml = mt->cache; mt_assoc_bt_lkup: while (ml != NULL) { if (cache_match_by_type(&jl_tupleref(types,0), types->length, (jl_tuple_t*)ml->sig, ml->va==jl_true)) { return ml->func; } ml = ml->next; } return NULL; }
// run time version of pointerset intrinsic (warning: x is not gc-rooted) JL_DLLEXPORT jl_value_t *jl_pointerset(jl_value_t *p, jl_value_t *x, jl_value_t *i, jl_value_t *align) { JL_TYPECHK(pointerset, pointer, p); JL_TYPECHK(pointerset, long, i); JL_TYPECHK(pointerref, long, align); jl_value_t *ety = jl_tparam0(jl_typeof(p)); if (ety == (jl_value_t*)jl_any_type) { jl_value_t **pp = (jl_value_t**)(jl_unbox_long(p) + (jl_unbox_long(i)-1)*sizeof(void*)); *pp = x; } else { if (!jl_is_datatype(ety)) jl_error("pointerset: invalid pointer"); size_t elsz = jl_datatype_size(ety); size_t nb = LLT_ALIGN(elsz, jl_datatype_align(ety)); char *pp = (char*)jl_unbox_long(p) + (jl_unbox_long(i)-1)*nb; if (jl_typeof(x) != ety) jl_error("pointerset: type mismatch in assign"); memcpy(pp, x, elsz); } return p; }
// ** NOTE: julia_mallocated means buffer was allocated using julia_malloc. // using the address of another array does not work!! jl_array_t *jl_ptr_to_array_1d(jl_type_t *atype, void *data, size_t nel, int julia_mallocated) { size_t elsz; jl_array_t *a; jl_type_t *el_type = (jl_type_t*)jl_tparam0(atype); int isunboxed = jl_is_bits_type(el_type); if (isunboxed) elsz = jl_bitstype_nbits(el_type)/8; else elsz = sizeof(void*); a = allocobj(sizeof(jl_array_t)); a->type = atype; a->data = data; a->length = nel; a->elsize = elsz; a->ptrarray = !isunboxed; a->ndims = 1; if (julia_mallocated) { a->reshaped = 0; jl_gc_acquire_buffer(data); } else { // this marks the array as not owning its buffer a->reshaped = 1; *((jl_array_t**)(&a->_space[0])) = a; } a->nrows = a->length; a->maxsize = a->length; a->offset = 0; return a; }
// own_buffer != 0 iff GC should call free() on this pointer eventually jl_array_t *jl_ptr_to_array_1d(jl_value_t *atype, void *data, size_t nel, int own_buffer) { size_t elsz; jl_array_t *a; jl_value_t *el_type = jl_tparam0(atype); int isunboxed = store_unboxed(el_type); if (isunboxed) elsz = jl_datatype_size(el_type); else elsz = sizeof(void*); a = allocobj((sizeof(jl_array_t)+jl_array_ndimwords(1)*sizeof(size_t)+15)&-16); a->type = atype; a->data = data; #ifdef STORE_ARRAY_LEN a->length = nel; #endif a->elsize = elsz; a->ptrarray = !isunboxed; a->ndims = 1; a->isshared = 1; a->isaligned = 0; // TODO: allow passing memalign'd buffers if (own_buffer) { a->how = 2; jl_gc_track_malloced_array(a); } else { a->how = 0; } a->nrows = nel; a->maxsize = nel; a->offset = 0; return a; }
// own_buffer != 0 iff GC should call free() on this pointer eventually jl_array_t *jl_ptr_to_array_1d(jl_type_t *atype, void *data, size_t nel, int own_buffer) { size_t elsz; jl_array_t *a; jl_type_t *el_type = (jl_type_t*)jl_tparam0(atype); int isunboxed = jl_is_bits_type(el_type); if (isunboxed) elsz = jl_bitstype_nbits(el_type)/8; else elsz = sizeof(void*); a = allocobj((sizeof(jl_array_t)+jl_array_ndimwords(1)*sizeof(size_t)+15)&-16); a->type = atype; a->data = data; a->length = nel; a->elsize = elsz; a->ptrarray = !isunboxed; a->ndims = 1; if (own_buffer) { a->ismalloc = 1; jl_array_data_owner(a) = (jl_value_t*)jl_gc_acquire_buffer(data,nel*elsz); } else { a->ismalloc = 0; jl_array_data_owner(a) = (jl_value_t*)a; } a->nrows = a->length; a->maxsize = a->length; a->offset = 0; return a; }
static union jl_typemap_t *mtcache_hash_bp(jl_array_t **pa, jl_value_t *ty, int8_t tparam, int8_t offs, jl_value_t *parent) { if (jl_is_datatype(ty)) { uintptr_t uid = ((jl_datatype_t*)ty)->uid; if (!uid || is_kind(ty) || jl_has_typevars(ty)) // be careful not to put non-leaf types or DataType/TypeConstructor in the cache here, // since they should have a lower priority and need to go into the sorted list return NULL; if (*pa == (void*)jl_nothing) { *pa = jl_alloc_vec_any(INIT_CACHE_SIZE); jl_gc_wb(parent, *pa); } while (1) { union jl_typemap_t *pml = &((union jl_typemap_t*)jl_array_data(*pa))[uid & ((*pa)->nrows-1)]; union jl_typemap_t ml = *pml; if (ml.unknown == NULL || ml.unknown == jl_nothing) { pml->unknown = jl_nothing; return pml; } jl_value_t *t; if (jl_typeof(ml.unknown) == (jl_value_t*)jl_typemap_level_type) { t = ml.node->key; } else { t = jl_field_type(ml.leaf->sig, offs); if (tparam) t = jl_tparam0(t); } if (t == ty) return pml; mtcache_rehash(pa, parent, tparam, offs); } } return NULL; }
static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, int tparam, int offs, struct typemap_intersection_env *closure) { size_t i, l = jl_array_len(a); jl_value_t **data = (jl_value_t**)jl_array_data(a); for (i = 0; i < l; i++) { union jl_typemap_t ml = ((union jl_typemap_t*)data)[i]; if (ml.unknown != NULL && ml.unknown != jl_nothing) { jl_value_t *t; if (jl_typeof(ml.unknown) == (jl_value_t*)jl_typemap_level_type) { t = ml.node->key; } else { t = jl_field_type(ml.leaf->sig, offs); if (tparam) t = jl_tparam0(t); } // TODO: fast path: test key `t` if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; } } return 1; }
static Type *julia_type_to_llvm(jl_value_t *jt) { if (jt == (jl_value_t*)jl_bool_type) return T_int1; if (jt == (jl_value_t*)jl_float32_type) return T_float32; if (jt == (jl_value_t*)jl_float64_type) return T_float64; if (jl_is_cpointer_type(jt)) { Type *lt = julia_type_to_llvm(jl_tparam0(jt)); if (lt == NULL) return NULL; if (lt == T_void) lt = T_int8; return PointerType::get(lt, 0); } if (jl_is_bits_type(jt)) { int nb = jl_bitstype_nbits(jt); if (nb == 8) return T_int8; if (nb == 16) return T_int16; if (nb == 32) return T_int32; if (nb == 64) return T_int64; else return Type::getIntNTy(getGlobalContext(), nb); } if (jt == (jl_value_t*)jl_bottom_type) return T_void; return jl_pvalue_llvmt; }
static jl_value_t *jl_deserialize_value(ios_t *s) { int pos = ios_pos(s); int32_t tag = read_uint8(s); if (tag == Null_tag) return NULL; if (tag == 0) { tag = read_uint8(s); return (jl_value_t*)ptrhash_get(&deser_tag, (void*)(ptrint_t)tag); } if (tag == BackRef_tag) { assert(tree_literal_values == NULL); ptrint_t offs = read_int32(s); void **bp = ptrhash_bp(&backref_table, (void*)(ptrint_t)offs); assert(*bp != HT_NOTFOUND); return (jl_value_t*)*bp; } jl_value_t *vtag=(jl_value_t*)ptrhash_get(&deser_tag,(void*)(ptrint_t)tag); if (tag >= VALUE_TAGS) { return vtag; } int usetable = (tree_literal_values == NULL); size_t i; if (vtag == (jl_value_t*)jl_tuple_type || vtag == (jl_value_t*)LongTuple_tag) { size_t len; if (vtag == (jl_value_t*)jl_tuple_type) len = read_uint8(s); else len = read_int32(s); jl_tuple_t *tu = jl_alloc_tuple_uninit(len); if (usetable) ptrhash_put(&backref_table, (void*)(ptrint_t)pos, (jl_value_t*)tu); for(i=0; i < len; i++) jl_tupleset(tu, i, jl_deserialize_value(s)); return (jl_value_t*)tu; } else if (vtag == (jl_value_t*)jl_symbol_type || vtag == (jl_value_t*)LongSymbol_tag) { size_t len; if (vtag == (jl_value_t*)jl_symbol_type) len = read_uint8(s); else len = read_int32(s); char *name = alloca(len+1); ios_read(s, name, len); name[len] = '\0'; jl_value_t *s = (jl_value_t*)jl_symbol(name); if (usetable) ptrhash_put(&backref_table, (void*)(ptrint_t)pos, s); return s; } else if (vtag == (jl_value_t*)jl_array_type) { jl_value_t *aty = jl_deserialize_value(s); jl_value_t *elty = jl_tparam0(aty); int16_t ndims = jl_unbox_long(jl_tparam1(aty)); size_t *dims = alloca(ndims*sizeof(size_t)); for(i=0; i < ndims; i++) dims[i] = jl_unbox_long(jl_deserialize_value(s)); jl_array_t *a = jl_new_array_((jl_type_t*)aty, ndims, dims); if (usetable) ptrhash_put(&backref_table, (void*)(ptrint_t)pos, (jl_value_t*)a); if (jl_is_bits_type(elty)) { size_t tot = a->length * a->elsize; ios_read(s, a->data, tot); } else { for(i=0; i < a->length; i++) { ((jl_value_t**)a->data)[i] = jl_deserialize_value(s); } } return (jl_value_t*)a; } else if (vtag == (jl_value_t*)jl_expr_type || vtag == (jl_value_t*)LongExpr_tag) { size_t len; if (vtag == (jl_value_t*)jl_expr_type) len = read_uint8(s); else len = read_int32(s); jl_expr_t *e = jl_exprn((jl_sym_t*)jl_deserialize_value(s), len); if (usetable) ptrhash_put(&backref_table, (void*)(ptrint_t)pos, (jl_value_t*)e); e->etype = jl_deserialize_value(s); for(i=0; i < len; i++) { jl_cellset(e->args, i, jl_deserialize_value(s)); } return (jl_value_t*)e; } else if (vtag == (jl_value_t*)LiteralVal_tag) { return jl_cellref(tree_literal_values, read_uint16(s)); } else if (vtag == (jl_value_t*)jl_tvar_type) { jl_tvar_t *tv = (jl_tvar_t*)newobj((jl_type_t*)jl_tvar_type, 4); if (usetable) ptrhash_put(&backref_table, (void*)(ptrint_t)pos, tv); tv->name = (jl_sym_t*)jl_deserialize_value(s); tv->lb = jl_deserialize_value(s); tv->ub = jl_deserialize_value(s); tv->bound = read_int8(s); return (jl_value_t*)tv; } else if (vtag == (jl_value_t*)jl_func_kind) { jl_value_t *ftype = jl_deserialize_value(s); jl_function_t *f = (jl_function_t*)newobj((jl_type_t*)ftype, 3); if (usetable) ptrhash_put(&backref_table, (void*)(ptrint_t)pos, f); f->linfo = (jl_lambda_info_t*)jl_deserialize_value(s); f->env = jl_deserialize_value(s); f->fptr = jl_deserialize_fptr(s); if (f->fptr == NULL) { f->fptr = &jl_trampoline; } return (jl_value_t*)f; } else if (vtag == (jl_value_t*)jl_lambda_info_type) { jl_lambda_info_t *li = (jl_lambda_info_t*)newobj((jl_type_t*)jl_lambda_info_type, LAMBDA_INFO_NW); if (usetable) ptrhash_put(&backref_table, (void*)(ptrint_t)pos, li); li->ast = jl_deserialize_value(s); li->sparams = (jl_tuple_t*)jl_deserialize_value(s); li->tfunc = jl_deserialize_value(s); li->name = (jl_sym_t*)jl_deserialize_value(s); li->specTypes = jl_deserialize_value(s); li->specializations = (jl_array_t*)jl_deserialize_value(s); li->inferred = jl_deserialize_value(s); li->file = jl_deserialize_value(s); li->line = jl_deserialize_value(s); li->module = (jl_module_t*)jl_deserialize_value(s); li->fptr = NULL; li->roots = NULL; li->functionObject = NULL; li->inInference = 0; li->inCompile = 0; li->unspecialized = NULL; return (jl_value_t*)li; } else if (vtag == (jl_value_t*)jl_module_type) { jl_module_t *m = jl_new_module(anonymous_sym); if (usetable) ptrhash_put(&backref_table, (void*)(ptrint_t)pos, m); m->name = (jl_sym_t*)jl_deserialize_value(s); while (1) { jl_value_t *name = jl_deserialize_value(s); if (name == NULL) break; jl_binding_t *b = jl_get_binding_wr(m, (jl_sym_t*)name); b->value = jl_deserialize_value(s); b->type = (jl_type_t*)jl_deserialize_value(s); b->constp = read_int8(s); b->exportp = read_int8(s); } while (1) { jl_value_t *name = jl_deserialize_value(s); if (name == NULL) break; jl_set_expander(m, (jl_sym_t*)name, (jl_function_t*)jl_deserialize_value(s)); } return (jl_value_t*)m; } else if (vtag == (jl_value_t*)jl_methtable_type) { jl_methtable_t *mt = (jl_methtable_t*)allocobj(sizeof(jl_methtable_t)); if (usetable) ptrhash_put(&backref_table, (void*)(ptrint_t)pos, mt); mt->type = (jl_type_t*)jl_methtable_type; mt->defs = jl_deserialize_methlist(s); mt->cache = jl_deserialize_methlist(s); mt->cache_1arg = (jl_array_t*)jl_deserialize_value(s); mt->max_args = read_int32(s); return (jl_value_t*)mt; } else if (vtag == (jl_value_t*)SmallInt64_tag) { jl_value_t *v = jl_box_int64(read_int32(s)); if (usetable) ptrhash_put(&backref_table, (void*)(ptrint_t)pos, v); return v; } else if (vtag == (jl_value_t*)jl_bits_kind) { jl_bits_type_t *bt = (jl_bits_type_t*)jl_deserialize_value(s); int nby = bt->nbits/8; char *data = alloca(nby); ios_read(s, data, nby); jl_value_t *v=NULL; if (bt == jl_int32_type) v = jl_box_int32(*(int32_t*)data); else if (bt == jl_int64_type) v = jl_box_int64(*(int64_t*)data); else if (bt == jl_bool_type) v = jl_box_bool(*(int8_t*)data); else { switch (bt->nbits) { case 8: v = jl_box8 (bt, *(int8_t*) data); break; case 16: v = jl_box16(bt, *(int16_t*)data); break; case 32: v = jl_box32(bt, *(int32_t*)data); break; case 64: v = jl_box64(bt, *(int64_t*)data); break; default: v = (jl_value_t*)allocobj(sizeof(void*)+nby); v->type = (jl_type_t*)bt; memcpy(jl_bits_data(v), data, nby); } } if (usetable) ptrhash_put(&backref_table, (void*)(ptrint_t)pos, v); return v; } else if (vtag == (jl_value_t*)jl_struct_kind) { jl_struct_type_t *typ = (jl_struct_type_t*)jl_deserialize_value(s); if (typ == jl_struct_kind || typ == jl_bits_kind) return jl_deserialize_tag_type(s, typ, pos); size_t nf = typ->names->length; jl_value_t *v = jl_new_struct_uninit(typ); if (usetable) ptrhash_put(&backref_table, (void*)(ptrint_t)pos, v); for(i=0; i < nf; i++) { ((jl_value_t**)v)[i+1] = jl_deserialize_value(s); } // TODO: put WeakRefs on the weak_refs list return v; } else if (vtag == (jl_value_t*)jl_tag_kind) { return jl_deserialize_tag_type(s, jl_tag_kind, pos); } assert(0); return NULL; }
JL_DLLEXPORT jl_array_t *jl_reshape_array(jl_value_t *atype, jl_array_t *data, jl_value_t *_dims) { jl_ptls_t ptls = jl_get_ptls_states(); jl_array_t *a; size_t ndims = jl_nfields(_dims); assert(is_ntuple_long(_dims)); size_t *dims = (size_t*)_dims; int ndimwords = jl_array_ndimwords(ndims); int tsz = JL_ARRAY_ALIGN(sizeof(jl_array_t) + ndimwords*sizeof(size_t) + sizeof(void*), JL_SMALL_BYTE_ALIGNMENT); a = (jl_array_t*)jl_gc_alloc(ptls, tsz, atype); // No allocation or safepoint allowed after this a->flags.pooled = tsz <= GC_MAX_SZCLASS; a->flags.ndims = ndims; a->offset = 0; a->data = NULL; a->flags.isaligned = data->flags.isaligned; jl_value_t *el_type = jl_tparam0(atype); assert(store_unboxed(el_type) == !data->flags.ptrarray); if (!data->flags.ptrarray) { a->elsize = jl_datatype_size(el_type); a->flags.ptrarray = 0; } else { a->elsize = sizeof(void*); a->flags.ptrarray = 1; } // if data is itself a shared wrapper, // owner should point back to the original array jl_array_data_owner(a) = jl_array_owner(data); a->flags.how = 3; a->data = data->data; a->flags.isshared = 1; data->flags.isshared = 1; if (ndims == 1) { size_t l = dims[0]; #ifdef STORE_ARRAY_LEN a->length = l; #endif a->nrows = l; a->maxsize = l; } else { size_t *adims = &a->nrows; size_t l = 1; wideint_t prod; for (size_t i = 0; i < ndims; i++) { adims[i] = dims[i]; prod = (wideint_t)l * (wideint_t)adims[i]; if (prod > (wideint_t) MAXINTVAL) jl_error("invalid Array dimensions"); l = prod; } #ifdef STORE_ARRAY_LEN a->length = l; #endif } return a; }
// ccall(pointer, rettype, (argtypes...), args...) static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) { JL_NARGSV(ccall, 3); jl_value_t *ptr=NULL, *rt=NULL, *at=NULL; Value *jl_ptr=NULL; JL_GC_PUSH(&ptr, &rt, &at); ptr = static_eval(args[1], ctx, true); if (ptr == NULL) { jl_value_t *ptr_ty = expr_type(args[1], ctx); Value *arg1 = emit_unboxed(args[1], ctx); if (!jl_is_cpointer_type(ptr_ty)) { emit_typecheck(arg1, (jl_value_t*)jl_voidpointer_type, "ccall: function argument not a pointer or valid constant", ctx); } jl_ptr = emit_unbox(T_size, T_psize, arg1); } rt = jl_interpret_toplevel_expr_in(ctx->module, args[2], &jl_tupleref(ctx->sp,0), jl_tuple_len(ctx->sp)/2); if (jl_is_tuple(rt)) { std::string msg = "in " + ctx->funcName + ": ccall: missing return type"; jl_error(msg.c_str()); } at = jl_interpret_toplevel_expr_in(ctx->module, args[3], &jl_tupleref(ctx->sp,0), jl_tuple_len(ctx->sp)/2); void *fptr=NULL; char *f_name=NULL, *f_lib=NULL; if (ptr != NULL) { if (jl_is_tuple(ptr) && jl_tuple_len(ptr)==1) { ptr = jl_tupleref(ptr,0); } if (jl_is_symbol(ptr)) f_name = ((jl_sym_t*)ptr)->name; else if (jl_is_byte_string(ptr)) f_name = jl_string_data(ptr); if (f_name != NULL) { // just symbol, default to JuliaDLHandle #ifdef __WIN32__ fptr = jl_dlsym_e(jl_dl_handle, f_name); if (!fptr) { //TODO: when one of these succeeds, store the f_lib name (and clear fptr) fptr = jl_dlsym_e(jl_kernel32_handle, f_name); if (!fptr) { fptr = jl_dlsym_e(jl_ntdll_handle, f_name); if (!fptr) { fptr = jl_dlsym_e(jl_crtdll_handle, f_name); if (!fptr) { fptr = jl_dlsym(jl_winsock_handle, f_name); } } } } else { // available in process symbol table fptr = NULL; } #else // will look in process symbol table #endif } else if (jl_is_cpointer_type(jl_typeof(ptr))) { fptr = *(void**)jl_bits_data(ptr); } else if (jl_is_tuple(ptr) && jl_tuple_len(ptr)>1) { jl_value_t *t0 = jl_tupleref(ptr,0); jl_value_t *t1 = jl_tupleref(ptr,1); if (jl_is_symbol(t0)) f_name = ((jl_sym_t*)t0)->name; else if (jl_is_byte_string(t0)) f_name = jl_string_data(t0); else JL_TYPECHK(ccall, symbol, t0); if (jl_is_symbol(t1)) f_lib = ((jl_sym_t*)t1)->name; else if (jl_is_byte_string(t1)) f_lib = jl_string_data(t1); else JL_TYPECHK(ccall, symbol, t1); } else { JL_TYPECHK(ccall, pointer, ptr); } } if (f_name == NULL && fptr == NULL && jl_ptr == NULL) { JL_GC_POP(); emit_error("ccall: null function pointer", ctx); return literal_pointer_val(jl_nothing); } JL_TYPECHK(ccall, type, rt); JL_TYPECHK(ccall, tuple, at); JL_TYPECHK(ccall, type, at); jl_tuple_t *tt = (jl_tuple_t*)at; std::vector<Type *> fargt(0); std::vector<Type *> fargt_sig(0); Type *lrt = julia_type_to_llvm(rt); if (lrt == NULL) { JL_GC_POP(); return literal_pointer_val(jl_nothing); } size_t i; bool haspointers = false; bool isVa = false; size_t nargt = jl_tuple_len(tt); std::vector<AttributeWithIndex> attrs; for(i=0; i < nargt; i++) { jl_value_t *tti = jl_tupleref(tt,i); if (jl_is_seq_type(tti)) { isVa = true; tti = jl_tparam0(tti); } if (jl_is_bits_type(tti)) { // see pull req #978. need to annotate signext/zeroext for // small integer arguments. jl_bits_type_t *bt = (jl_bits_type_t*)tti; if (bt->nbits < 32) { if (jl_signed_type == NULL) { jl_signed_type = jl_get_global(jl_core_module,jl_symbol("Signed")); } #ifdef LLVM32 Attributes::AttrVal av; if (jl_signed_type && jl_subtype(tti, jl_signed_type, 0)) av = Attributes::SExt; else av = Attributes::ZExt; attrs.push_back(AttributeWithIndex::get(getGlobalContext(), i+1, ArrayRef<Attributes::AttrVal>(&av, 1))); #else Attribute::AttrConst av; if (jl_signed_type && jl_subtype(tti, jl_signed_type, 0)) av = Attribute::SExt; else av = Attribute::ZExt; attrs.push_back(AttributeWithIndex::get(i+1, av)); #endif } } Type *t = julia_type_to_llvm(tti); if (t == NULL) { JL_GC_POP(); return literal_pointer_val(jl_nothing); } fargt.push_back(t); if (!isVa) fargt_sig.push_back(t); } // check for calling convention specifier CallingConv::ID cc = CallingConv::C; jl_value_t *last = args[nargs]; if (jl_is_expr(last)) { jl_sym_t *lhd = ((jl_expr_t*)last)->head; if (lhd == jl_symbol("stdcall")) { cc = CallingConv::X86_StdCall; nargs--; } else if (lhd == jl_symbol("cdecl")) { cc = CallingConv::C; nargs--; } else if (lhd == jl_symbol("fastcall")) { cc = CallingConv::X86_FastCall; nargs--; } else if (lhd == jl_symbol("thiscall")) { cc = CallingConv::X86_ThisCall; nargs--; } } if ((!isVa && jl_tuple_len(tt) != (nargs-2)/2) || ( isVa && jl_tuple_len(tt)-1 > (nargs-2)/2)) jl_error("ccall: wrong number of arguments to C function"); // some special functions if (fptr == &jl_array_ptr) { Value *ary = emit_expr(args[4], ctx); JL_GC_POP(); return mark_julia_type(builder.CreateBitCast(emit_arrayptr(ary),lrt), rt); } // see if there are & arguments for(i=4; i < nargs+1; i+=2) { jl_value_t *argi = args[i]; if (jl_is_expr(argi) && ((jl_expr_t*)argi)->head == amp_sym) { haspointers = true; break; } } // make LLVM function object for the target Value *llvmf; FunctionType *functype = FunctionType::get(lrt, fargt_sig, isVa); if (jl_ptr != NULL) { null_pointer_check(jl_ptr,ctx); Type *funcptype = PointerType::get(functype,0); llvmf = builder.CreateIntToPtr(jl_ptr, funcptype); } else if (fptr != NULL) { Type *funcptype = PointerType::get(functype,0); llvmf = literal_pointer_val(fptr, funcptype); } else { void *symaddr; if (f_lib != NULL) symaddr = add_library_sym(f_name, f_lib); else symaddr = sys::DynamicLibrary::SearchForAddressOfSymbol(f_name); if (symaddr == NULL) { JL_GC_POP(); std::stringstream msg; msg << "ccall: could not find function "; msg << f_name; if (f_lib != NULL) { msg << " in library "; msg << f_lib; } emit_error(msg.str(), ctx); return literal_pointer_val(jl_nothing); } llvmf = jl_Module->getOrInsertFunction(f_name, functype); } // save temp argument area stack pointer Value *saveloc=NULL; Value *stacksave=NULL; if (haspointers) { // TODO: inline this saveloc = builder.CreateCall(save_arg_area_loc_func); stacksave = builder.CreateCall(Intrinsic::getDeclaration(jl_Module, Intrinsic::stacksave)); } // emit arguments Value *argvals[(nargs-3)/2]; int last_depth = ctx->argDepth; int nargty = jl_tuple_len(tt); for(i=4; i < nargs+1; i+=2) { int ai = (i-4)/2; jl_value_t *argi = args[i]; bool addressOf = false; if (jl_is_expr(argi) && ((jl_expr_t*)argi)->head == amp_sym) { addressOf = true; argi = jl_exprarg(argi,0); } Type *largty; jl_value_t *jargty; if (isVa && ai >= nargty-1) { largty = fargt[nargty-1]; jargty = jl_tparam0(jl_tupleref(tt,nargty-1)); } else { largty = fargt[ai]; jargty = jl_tupleref(tt,ai); } Value *arg; if (largty == jl_pvalue_llvmt) { arg = emit_expr(argi, ctx, true); } else { arg = emit_unboxed(argi, ctx); if (jl_is_bits_type(expr_type(argi, ctx))) { if (addressOf) arg = emit_unbox(largty->getContainedType(0), largty, arg); else arg = emit_unbox(largty, PointerType::get(largty,0), arg); } } /* #ifdef JL_GC_MARKSWEEP // make sure args are rooted if (largty->isPointerTy() && (largty == jl_pvalue_llvmt || !jl_is_bits_type(expr_type(args[i], ctx)))) { make_gcroot(boxed(arg), ctx); } #endif */ argvals[ai] = julia_to_native(largty, jargty, arg, argi, addressOf, ai+1, ctx); } // the actual call Value *result = builder.CreateCall(llvmf, ArrayRef<Value*>(&argvals[0],(nargs-3)/2)); if (cc != CallingConv::C) ((CallInst*)result)->setCallingConv(cc); #ifdef LLVM32 ((CallInst*)result)->setAttributes(AttrListPtr::get(getGlobalContext(), ArrayRef<AttributeWithIndex>(attrs))); #else ((CallInst*)result)->setAttributes(AttrListPtr::get(attrs.data(),attrs.size())); #endif // restore temp argument area stack pointer if (haspointers) { assert(saveloc != NULL); builder.CreateCall(restore_arg_area_loc_func, saveloc); assert(stacksave != NULL); builder.CreateCall(Intrinsic::getDeclaration(jl_Module, Intrinsic::stackrestore), stacksave); } ctx->argDepth = last_depth; if (0) { // Enable this to turn on SSPREQ (-fstack-protector) on the function containing this ccall #ifdef LLVM32 ctx->f->addFnAttr(Attributes::StackProtectReq); #else ctx->f->addFnAttr(Attribute::StackProtectReq); #endif } JL_GC_POP(); if (lrt == T_void) return literal_pointer_val((jl_value_t*)jl_nothing); return mark_julia_type(result, rt); }
static Value *julia_to_native(Type *ty, jl_value_t *jt, Value *jv, jl_value_t *argex, bool addressOf, int argn, jl_codectx_t *ctx) { Type *vt = jv->getType(); if (ty == jl_pvalue_llvmt) { return boxed(jv); } else if (ty == vt && !addressOf) { return jv; } else if (vt != jl_pvalue_llvmt) { // argument value is unboxed if (addressOf) { if (ty->isPointerTy() && ty->getContainedType(0)==vt) { // pass the address of an alloca'd thing, not a box // since those are immutable. Value *slot = builder.CreateAlloca(vt); builder.CreateStore(jv, slot); return builder.CreateBitCast(slot, ty); } } else if ((vt->isIntegerTy() && ty->isIntegerTy()) || (vt->isFloatingPointTy() && ty->isFloatingPointTy()) || (vt->isPointerTy() && ty->isPointerTy())) { if (vt->getPrimitiveSizeInBits() == ty->getPrimitiveSizeInBits()) { return builder.CreateBitCast(jv, ty); } } // error. box for error handling. jv = boxed(jv); } else if (jl_is_cpointer_type(jt)) { jl_value_t *aty = expr_type(argex, ctx); if (jl_is_array_type(aty) && (jl_tparam0(jt) == jl_tparam0(aty) || jl_tparam0(jt) == (jl_value_t*)jl_bottom_type)) { // array to pointer return builder.CreateBitCast(emit_arrayptr(jv), ty); } if (aty == (jl_value_t*)jl_ascii_string_type || aty == (jl_value_t*)jl_utf8_string_type) { return builder.CreateBitCast(emit_arrayptr(emit_nthptr(jv,1)), ty); } Value *p = builder.CreateCall4(value_to_pointer_func, literal_pointer_val(jl_tparam0(jt)), jv, ConstantInt::get(T_int32, argn), ConstantInt::get(T_int32, (int)addressOf)); assert(ty->isPointerTy()); return builder.CreateBitCast(p, ty); } // TODO: error for & with non-pointer argument type assert(jl_is_bits_type(jt)); std::stringstream msg; msg << "ccall argument "; msg << argn; emit_typecheck(jv, jt, msg.str(), ctx); Value *p = bitstype_pointer(jv); return builder.CreateLoad(builder.CreateBitCast(p, PointerType::get(ty,0)), false); }
static Value *julia_to_native(Type *ty, jl_value_t *jt, Value *jv, jl_value_t *argex, bool addressOf, int argn, jl_codectx_t *ctx, bool *mightNeedTempSpace, bool *needStackRestore) { Type *vt = jv->getType(); if (ty == jl_pvalue_llvmt) { return boxed(jv,ctx); } else if (ty == vt && !addressOf) { return jv; } else if (vt != jl_pvalue_llvmt) { // argument value is unboxed if (addressOf) { if (ty->isPointerTy() && ty->getContainedType(0)==vt) { // pass the address of an alloca'd thing, not a box // since those are immutable. *needStackRestore = true; Value *slot = builder.CreateAlloca(vt); builder.CreateStore(jv, slot); return builder.CreateBitCast(slot, ty); } } else if ((vt->isIntegerTy() && ty->isIntegerTy()) || (vt->isFloatingPointTy() && ty->isFloatingPointTy()) || (vt->isPointerTy() && ty->isPointerTy())) { if (vt->getPrimitiveSizeInBits() == ty->getPrimitiveSizeInBits()) { return builder.CreateBitCast(jv, ty); } } // error. box for error handling. jv = boxed(jv,ctx); } else if (jl_is_cpointer_type(jt)) { assert(ty->isPointerTy()); jl_value_t *aty = expr_type(argex, ctx); if (jl_is_array_type(aty) && (jl_tparam0(jt) == jl_tparam0(aty) || jl_tparam0(jt) == (jl_value_t*)jl_bottom_type)) { // array to pointer return builder.CreateBitCast(emit_arrayptr(jv), ty); } if (aty == (jl_value_t*)jl_ascii_string_type || aty == (jl_value_t*)jl_utf8_string_type) { return builder.CreateBitCast(emit_arrayptr(emit_nthptr(jv,1,tbaa_const)), ty); } if (jl_is_structtype(aty) && jl_is_leaf_type(aty) && !jl_is_array_type(aty)) { if (!addressOf) { emit_error("ccall: expected & on argument", ctx); return literal_pointer_val(jl_nothing); } return builder.CreateBitCast(emit_nthptr_addr(jv, (size_t)1), ty); // skip type tag field } *mightNeedTempSpace = true; Value *p = builder.CreateCall4(prepare_call(value_to_pointer_func), literal_pointer_val(jl_tparam0(jt)), jv, ConstantInt::get(T_int32, argn), ConstantInt::get(T_int32, (int)addressOf)); return builder.CreateBitCast(p, ty); } else if (jl_is_structtype(jt)) { if (addressOf) jl_error("ccall: unexpected & on argument"); // the only "safe" thing to emit here is the expected struct assert (ty->isStructTy() && (Type*)((jl_datatype_t*)jt)->struct_decl == ty); jl_value_t *aty = expr_type(argex, ctx); if (aty != jt) { std::stringstream msg; msg << "ccall argument "; msg << argn; emit_typecheck(jv, jt, msg.str(), ctx); } //TODO: check instead that prefix matches //if (!jl_is_structtype(aty)) // emit_typecheck(emit_typeof(jv), (jl_value_t*)jl_struct_kind, "ccall: Struct argument called with something that isn't a struct", ctx); // //safe thing would be to also check that jl_typeof(aty)->size > sizeof(ty) here and/or at runtime Value *pjv = builder.CreateBitCast(emit_nthptr_addr(jv, (size_t)1), PointerType::get(ty,0)); return builder.CreateLoad(pjv, false); } else if (jl_is_tuple(jt)) { return emit_unbox(ty,jv,jt); } // TODO: error for & with non-pointer argument type assert(jl_is_bitstype(jt)); std::stringstream msg; msg << "ccall argument "; msg << argn; emit_typecheck(jv, jt, msg.str(), ctx); Value *p = data_pointer(jv); return builder.CreateLoad(builder.CreateBitCast(p, PointerType::get(ty,0)), false); }
jl_array_t *jl_reshape_array(jl_type_t *atype, jl_array_t *data, jl_tuple_t *dims) { size_t i; jl_array_t *a; size_t ndims = jl_tuple_len(dims); int ndimwords = jl_array_ndimwords(ndims); a = allocobj((sizeof(jl_array_t) + ndimwords*sizeof(size_t) + 15)&-16); a->type = atype; a->ndims = ndims; a->data = NULL; JL_GC_PUSH(&a); char *d = data->data; if (data->ndims == 1) d -= data->offset*data->elsize; if (d == jl_array_inline_data_area(data)) { if (data->ndims == 1) { // data might resize, so switch it to shared representation. // problem: we'd like to do that, but it might not be valid, // since the buffer might be used from C in a way that it's // assumed not to move. for now, just copy the data (note this // case only happens for sizes <= ARRAY_INLINE_NBYTES) jl_mallocptr_t *mp = array_new_buffer(data, data->length); memcpy(mp->ptr, data->data, data->length * data->elsize); a->data = mp->ptr; jl_array_data_owner(a) = (jl_value_t*)mp; a->ismalloc = 1; //data->data = mp->ptr; //data->offset = 0; //data->maxsize = data->length; //jl_array_data_owner(data) = (jl_value_t*)mp; } else { a->ismalloc = 0; jl_array_data_owner(a) = (jl_value_t*)data; } } else { a->ismalloc = data->ismalloc; jl_array_data_owner(a) = jl_array_data_owner(data); } if (a->data == NULL) a->data = data->data; jl_type_t *el_type = (jl_type_t*)jl_tparam0(atype); if (jl_is_bits_type(el_type)) { a->elsize = jl_bitstype_nbits(el_type)/8; a->ptrarray = 0; } else { a->elsize = sizeof(void*); a->ptrarray = 1; } if (ndims == 1) { a->length = jl_unbox_long(jl_tupleref(dims,0)); a->nrows = a->length; a->maxsize = a->length; a->offset = 0; } else { size_t *adims = &a->nrows; size_t l=1; for(i=0; i < ndims; i++) { adims[i] = jl_unbox_long(jl_tupleref(dims, i)); l *= adims[i]; } a->length = l; } JL_GC_POP(); return a; }
static jl_array_t *_new_array(jl_type_t *atype, uint32_t ndims, size_t *dims) { size_t i, tot, nel=1; int isunboxed=0, elsz; void *data; jl_array_t *a; for(i=0; i < ndims; i++) { nel *= dims[i]; } jl_type_t *el_type = (jl_type_t*)jl_tparam0(atype); isunboxed = jl_is_bits_type(el_type); if (isunboxed) { elsz = jl_bitstype_nbits(el_type)/8; tot = elsz * nel; if (elsz == 1) { // hidden 0 terminator for all byte arrays tot++; } } else { elsz = sizeof(void*); tot = sizeof(void*) * nel; } int ndimwords = jl_array_ndimwords(ndims); if (tot <= ARRAY_INLINE_NBYTES) { size_t tsz = tot>sizeof(size_t) ? tot-sizeof(size_t) : tot; a = allocobj((sizeof(jl_array_t)+tsz+ndimwords*sizeof(size_t)+15)&-16); a->type = atype; a->ismalloc = 0; data = (&a->_space[0] + ndimwords*sizeof(size_t)); if (tot > 0 && !isunboxed) { memset(data, 0, tot); } } else { a = allocobj((sizeof(jl_array_t)+ndimwords*sizeof(size_t)+15)&-16); JL_GC_PUSH(&a); a->type = atype; a->ismalloc = 1; // temporarily initialize to make gc-safe a->data = NULL; jl_value_t **powner = (jl_value_t**)(&a->_space[0] + ndimwords*sizeof(size_t)); *powner = (jl_value_t*)jl_gc_managed_malloc(tot); data = ((jl_mallocptr_t*)*powner)->ptr; if (!isunboxed) memset(data, 0, tot); JL_GC_POP(); } a->data = data; if (elsz == 1) ((char*)data)[tot-1] = '\0'; a->length = nel; a->ndims = ndims; a->ptrarray = !isunboxed; a->elsize = elsz; if (ndims == 1) { a->nrows = nel; a->maxsize = nel; a->offset = 0; } else { size_t *adims = &a->nrows; for(i=0; i < ndims; i++) adims[i] = dims[i]; } return a; }
// ccall(pointer, rettype, (argtypes...), args...) static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) { JL_NARGSV(ccall, 3); jl_value_t *ptr=NULL, *rt=NULL, *at=NULL; JL_GC_PUSH(&ptr, &rt, &at); ptr = jl_interpret_toplevel_expr_in(ctx->module, args[1], &jl_tupleref(ctx->sp,0), ctx->sp->length/2); rt = jl_interpret_toplevel_expr_in(ctx->module, args[2], &jl_tupleref(ctx->sp,0), ctx->sp->length/2); if (jl_is_tuple(rt)) { std::string msg = "in " + ctx->funcName + ": ccall: missing return type"; jl_error(msg.c_str()); } at = jl_interpret_toplevel_expr_in(ctx->module, args[3], &jl_tupleref(ctx->sp,0), ctx->sp->length/2); void *fptr; if (jl_is_symbol(ptr)) { // just symbol, default to JuliaDLHandle fptr = jl_dlsym(jl_dl_handle, ((jl_sym_t*)ptr)->name); } else { JL_TYPECHK(ccall, pointer, ptr); fptr = *(void**)jl_bits_data(ptr); } JL_TYPECHK(ccall, type, rt); JL_TYPECHK(ccall, tuple, at); JL_TYPECHK(ccall, type, at); jl_tuple_t *tt = (jl_tuple_t*)at; std::vector<Type *> fargt(0); std::vector<Type *> fargt_sig(0); Type *lrt = julia_type_to_llvm(rt, ctx); if (lrt == NULL) { JL_GC_POP(); return literal_pointer_val(jl_nothing); } size_t i; bool haspointers = false; bool isVa = false; for(i=0; i < tt->length; i++) { jl_value_t *tti = jl_tupleref(tt,i); if (jl_is_seq_type(tti)) { isVa = true; tti = jl_tparam0(tti); } Type *t = julia_type_to_llvm(tti, ctx); if (t == NULL) { JL_GC_POP(); return literal_pointer_val(jl_nothing); } fargt.push_back(t); if (!isVa) fargt_sig.push_back(t); } if ((!isVa && tt->length != (nargs-2)/2) || ( isVa && tt->length-1 > (nargs-2)/2)) jl_error("ccall: wrong number of arguments to C function"); // some special functions if (fptr == &jl_array_ptr) { Value *ary = emit_expr(args[4], ctx, true); JL_GC_POP(); return mark_julia_type(builder.CreateBitCast(emit_arrayptr(ary),T_pint8), rt); } // see if there are & arguments for(i=4; i < nargs+1; i+=2) { jl_value_t *argi = args[i]; if (jl_is_expr(argi) && ((jl_expr_t*)argi)->head == amp_sym) { haspointers = true; break; } } // make LLVM function object for the target Function *llvmf = Function::Create(FunctionType::get(lrt, fargt_sig, isVa), Function::ExternalLinkage, "ccall_", jl_Module); jl_ExecutionEngine->addGlobalMapping(llvmf, fptr); // save temp argument area stack pointer Value *saveloc=NULL; Value *stacksave=NULL; if (haspointers) { // TODO: inline this saveloc = builder.CreateCall(save_arg_area_loc_func); stacksave = builder.CreateCall(Intrinsic::getDeclaration(jl_Module, Intrinsic::stacksave)); } // emit arguments Value *argvals[(nargs-3)/2]; int last_depth = ctx->argDepth; int nargty = tt->length; for(i=4; i < nargs+1; i+=2) { int ai = (i-4)/2; jl_value_t *argi = args[i]; bool addressOf = false; if (jl_is_expr(argi) && ((jl_expr_t*)argi)->head == amp_sym) { addressOf = true; argi = jl_exprarg(argi,0); } Value *arg = emit_expr(argi, ctx, true); Type *largty; jl_value_t *jargty; if (isVa && ai >= nargty-1) { largty = fargt[nargty-1]; jargty = jl_tparam0(jl_tupleref(tt,nargty-1)); } else { largty = fargt[ai]; jargty = jl_tupleref(tt,ai); } /* #ifdef JL_GC_MARKSWEEP // make sure args are rooted if (largty->isPointerTy() && (largty == jl_pvalue_llvmt || !jl_is_bits_type(expr_type(args[i], ctx)))) { make_gcroot(boxed(arg), ctx); } #endif */ argvals[ai] = julia_to_native(largty, jargty, arg, argi, addressOf, ai+1, ctx); } // the actual call Value *result = builder.CreateCall(llvmf, ArrayRef<Value*>(&argvals[0],(nargs-3)/2)); // restore temp argument area stack pointer if (haspointers) { assert(saveloc != NULL); builder.CreateCall(restore_arg_area_loc_func, saveloc); assert(stacksave != NULL); builder.CreateCall(Intrinsic::getDeclaration(jl_Module, Intrinsic::stackrestore), stacksave); } ctx->argDepth = last_depth; JL_GC_POP(); if (lrt == T_void) return literal_pointer_val((jl_value_t*)jl_nothing); return mark_julia_type(result, rt); }
JL_DLLEXPORT void *jl_array_eltype(jl_value_t *a) { return jl_tparam0(jl_typeof(a)); }