Skip to content

Commit

Permalink
convert some typeof tags to small integers
Browse files Browse the repository at this point in the history
The value here is two-fold. One, we speed up sysimg load times slightly,
since we can entirely skip relocations for these values and avoid
faulting in some writable pages. This should let us (later) move more
content into the ConstData section, such as String objects. Secondly,
some type-manipulation native code becomes simpler, since it is based on
small consecutive constants instead of requiring extra pointer loads to
check for pointer identity. This is similar to the existing small-union
optimization, but for type tags, and only for specific ones.

This makes use of the fact that the zero page (about 4096 byes) cannot
be allocated in the usual C memory model, which lets us define up to 255
of these special types, after taking into account the 4 gc bits. As a
current implementation limitation for performance, these cannot be
parametric types.

Note there are probably more places in Base that can be updated to use
`jl_typetagof` instead of `jl_typeof`, where we know if it is a type or tag.
For example, this optimize most of builtins.c file, except for the
`jl_object_id` function.
  • Loading branch information
vtjnash committed May 6, 2023
1 parent 7cf01e5 commit e26b63c
Show file tree
Hide file tree
Showing 38 changed files with 937 additions and 653 deletions.
19 changes: 9 additions & 10 deletions base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,6 @@ ccall(:jl_toplevel_eval_in, Any, (Any, Any),
(f::typeof(Typeof))(x) = ($(_expr(:meta,:nospecialize,:x)); isa(x,Type) ? Type{x} : typeof(x))
end)


macro nospecialize(x)
_expr(:meta, :nospecialize, x)
end
Expand All @@ -256,7 +255,15 @@ TypeVar(n::Symbol, @nospecialize(lb), @nospecialize(ub)) = _typevar(n, lb, ub)

UnionAll(v::TypeVar, @nospecialize(t)) = ccall(:jl_type_unionall, Any, (Any, Any), v, t)

const Vararg = ccall(:jl_toplevel_eval_in, Any, (Any, Any), Core, _expr(:new, TypeofVararg))
# simple convert for use by constructors of types in Core
# note that there is no actual conversion defined here,
# so the methods and ccall's in Core aren't permitted to use convert
convert(::Type{Any}, @nospecialize(x)) = x
convert(::Type{T}, x::T) where {T} = x
cconvert(::Type{T}, x) where {T} = convert(T, x)
unsafe_convert(::Type{T}, x::T) where {T} = x

const Vararg = ccall(:jl_wrap_vararg, Any, (Int, Int), 0, 0)

# dispatch token indicating a kwarg (keyword sorter) call
function kwcall end
Expand Down Expand Up @@ -448,14 +455,6 @@ function _Task(@nospecialize(f), reserved_stack::Int, completion_future)
return ccall(:jl_new_task, Ref{Task}, (Any, Any, Int), f, completion_future, reserved_stack)
end

# simple convert for use by constructors of types in Core
# note that there is no actual conversion defined here,
# so the methods and ccall's in Core aren't permitted to use convert
convert(::Type{Any}, @nospecialize(x)) = x
convert(::Type{T}, x::T) where {T} = x
cconvert(::Type{T}, x) where {T} = convert(T, x)
unsafe_convert(::Type{T}, x::T) where {T} = x

_is_internal(__module__) = __module__ === Core
# can be used in place of `@assume_effects :foldable` (supposed to be used for bootstrapping)
macro _foldable_meta()
Expand Down
4 changes: 4 additions & 0 deletions cli/jl_exports.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ JL_EXPORTED_DATA_POINTERS(XX)
JL_EXPORTED_DATA_SYMBOLS(XX)
#undef XX

// define a copy of exported data
#define jl_max_tags 64
JL_DLLEXPORT void *small_typeof[(jl_max_tags << 4) / sizeof(void*)]; // 16-bit aligned, like the GC

// Declare list of exported functions (sans type)
#define XX(name) JL_DLLEXPORT void name(void);
typedef void (anonfunc)(void);
Expand Down
18 changes: 17 additions & 1 deletion src/aotcompile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1575,6 +1575,14 @@ void jl_dump_native_impl(void *native_code,
GlobalVariable::ExternalLinkage,
jlRTLD_DEFAULT_var,
"jl_RTLD_DEFAULT_handle_pointer"), TheTriple);

// let the compiler know we are going to internalize a copy of this,
// if it has a current usage with ExternalLinkage
auto small_typeof_copy = dataM->getGlobalVariable("small_typeof");
if (small_typeof_copy) {
small_typeof_copy->setVisibility(GlobalValue::HiddenVisibility);
small_typeof_copy->setDSOLocal(true);
}
}

// Reserve space for the output files and names
Expand Down Expand Up @@ -1651,13 +1659,21 @@ void jl_dump_native_impl(void *native_code,
auto shards = emit_shard_table(*sysimageM, T_size, T_psize, threads);
auto ptls = emit_ptls_table(*sysimageM, T_size, T_psize);
auto header = emit_image_header(*sysimageM, threads, nfvars, ngvars);
auto AT = ArrayType::get(T_psize, 4);
auto AT = ArrayType::get(T_size, sizeof(small_typeof) / sizeof(void*));
auto small_typeof_copy = new GlobalVariable(*sysimageM, AT, false,
GlobalVariable::ExternalLinkage,
Constant::getNullValue(AT),
"small_typeof");
small_typeof_copy->setVisibility(GlobalValue::HiddenVisibility);
small_typeof_copy->setDSOLocal(true);
AT = ArrayType::get(T_psize, 5);
auto pointers = new GlobalVariable(*sysimageM, AT, false,
GlobalVariable::ExternalLinkage,
ConstantArray::get(AT, {
ConstantExpr::getBitCast(header, T_psize),
ConstantExpr::getBitCast(shards, T_psize),
ConstantExpr::getBitCast(ptls, T_psize),
ConstantExpr::getBitCast(small_typeof_copy, T_psize),
ConstantExpr::getBitCast(target_ids, T_psize)
}),
"jl_image_pointers");
Expand Down
8 changes: 4 additions & 4 deletions src/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ JL_DLLEXPORT jl_value_t *jl_alloc_string(size_t len)
jl_throw(jl_memory_exception);
s = jl_gc_big_alloc_noinline(ptls, allocsz);
}
jl_set_typeof(s, jl_string_type);
jl_set_typetagof(s, jl_string_tag, 0);
maybe_record_alloc_to_profile(s, len, jl_string_type);
*(size_t*)s = len;
jl_string_data(s)[len] = 0;
Expand Down Expand Up @@ -1255,16 +1255,16 @@ JL_DLLEXPORT void jl_array_ptr_copy(jl_array_t *dest, void **dest_p,

JL_DLLEXPORT void jl_array_ptr_1d_push(jl_array_t *a, jl_value_t *item)
{
assert(jl_typeis(a, jl_array_any_type));
assert(jl_typetagis(a, jl_array_any_type));
jl_array_grow_end(a, 1);
size_t n = jl_array_nrows(a);
jl_array_ptr_set(a, n - 1, item);
}

JL_DLLEXPORT void jl_array_ptr_1d_append(jl_array_t *a, jl_array_t *a2)
{
assert(jl_typeis(a, jl_array_any_type));
assert(jl_typeis(a2, jl_array_any_type));
assert(jl_typetagis(a, jl_array_any_type));
assert(jl_typetagis(a2, jl_array_any_type));
size_t i;
size_t n = jl_array_nrows(a);
size_t n2 = jl_array_nrows(a2);
Expand Down
22 changes: 11 additions & 11 deletions src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -700,11 +700,11 @@ static value_t julia_to_scm_noalloc(fl_context_t *fl_ctx, jl_value_t *v, int che
if (julia_to_scm_noalloc1(fl_ctx, v, &retval))
return retval;
assert(!jl_is_expr(v) &&
!jl_typeis(v, jl_linenumbernode_type) &&
!jl_typeis(v, jl_gotonode_type) &&
!jl_typeis(v, jl_quotenode_type) &&
!jl_typeis(v, jl_newvarnode_type) &&
!jl_typeis(v, jl_globalref_type));
!jl_typetagis(v, jl_linenumbernode_type) &&
!jl_typetagis(v, jl_gotonode_type) &&
!jl_typetagis(v, jl_quotenode_type) &&
!jl_typetagis(v, jl_newvarnode_type) &&
!jl_typetagis(v, jl_globalref_type));
return julia_to_scm_noalloc2(fl_ctx, v, check_valid);
}

Expand Down Expand Up @@ -745,7 +745,7 @@ static value_t julia_to_scm_(fl_context_t *fl_ctx, jl_value_t *v, int check_vali
// GC Note: jl_fieldref(v, 0) allocates for GotoNode
// but we don't need a GC root here because julia_to_list2_noalloc
// shouldn't allocate in this case.
if (jl_typeis(v, jl_linenumbernode_type)) {
if (jl_typetagis(v, jl_linenumbernode_type)) {
jl_value_t *file = jl_fieldref_noalloc(v,1);
jl_value_t *line = jl_fieldref(v,0);
value_t args = julia_to_list2_noalloc(fl_ctx, line, file, check_valid);
Expand All @@ -755,13 +755,13 @@ static value_t julia_to_scm_(fl_context_t *fl_ctx, jl_value_t *v, int check_vali
fl_free_gc_handles(fl_ctx, 1);
return scmv;
}
if (jl_typeis(v, jl_gotonode_type))
if (jl_typetagis(v, jl_gotonode_type))
return julia_to_list2_noalloc(fl_ctx, (jl_value_t*)jl_goto_sym, jl_fieldref(v,0), check_valid);
if (jl_typeis(v, jl_quotenode_type))
if (jl_typetagis(v, jl_quotenode_type))
return julia_to_list2(fl_ctx, (jl_value_t*)jl_inert_sym, jl_fieldref_noalloc(v,0), 0);
if (jl_typeis(v, jl_newvarnode_type))
if (jl_typetagis(v, jl_newvarnode_type))
return julia_to_list2_noalloc(fl_ctx, (jl_value_t*)jl_newvar_sym, jl_fieldref(v,0), check_valid);
if (jl_typeis(v, jl_globalref_type)) {
if (jl_typetagis(v, jl_globalref_type)) {
jl_module_t *m = jl_globalref_mod(v);
jl_sym_t *sym = jl_globalref_name(v);
if (m == jl_core_module)
Expand Down Expand Up @@ -1011,7 +1011,7 @@ static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule
// __source__ argument
jl_value_t *lno = jl_array_ptr_ref(args, 1);
margs[1] = lno;
if (!jl_typeis(lno, jl_linenumbernode_type)) {
if (!jl_typetagis(lno, jl_linenumbernode_type)) {
margs[1] = jl_new_struct(jl_linenumbernode_type, jl_box_long(0), jl_nothing);
}
margs[2] = (jl_value_t*)inmodule;
Expand Down
117 changes: 75 additions & 42 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ extern "C" {
static int bits_equal(const void *a, const void *b, int sz) JL_NOTSAFEPOINT
{
switch (sz) {
case 1: return *(int8_t*)a == *(int8_t*)b;
// Let compiler constant folds the following.
case 1: return *(uint8_t*)a == *(uint8_t*)b;
// Let compiler constant folds the following, though we may not know alignment of them
case 2: return memcmp(a, b, 2) == 0;
case 4: return memcmp(a, b, 4) == 0;
case 8: return memcmp(a, b, 8) == 0;
Expand Down Expand Up @@ -147,10 +147,10 @@ static int egal_types(const jl_value_t *a, const jl_value_t *b, jl_typeenv_t *en
{
if (a == b)
return 1;
jl_datatype_t *dt = (jl_datatype_t*)jl_typeof(a);
if (dt != (jl_datatype_t*)jl_typeof(b))
uintptr_t dtag = jl_typetagof(a);
if (dtag != jl_typetagof(b))
return 0;
if (dt == jl_datatype_type) {
if (dtag == jl_datatype_tag << 4) {
jl_datatype_t *dta = (jl_datatype_t*)a;
jl_datatype_t *dtb = (jl_datatype_t*)b;
if (dta->name != dtb->name)
Expand All @@ -164,7 +164,7 @@ static int egal_types(const jl_value_t *a, const jl_value_t *b, jl_typeenv_t *en
}
return 1;
}
if (dt == jl_tvar_type) {
if (dtag == jl_tvar_tag << 4) {
jl_typeenv_t *pe = env;
while (pe != NULL) {
if (pe->var == (jl_tvar_t*)a)
Expand All @@ -173,7 +173,7 @@ static int egal_types(const jl_value_t *a, const jl_value_t *b, jl_typeenv_t *en
}
return 0;
}
if (dt == jl_unionall_type) {
if (dtag == jl_unionall_tag << 4) {
jl_unionall_t *ua = (jl_unionall_t*)a;
jl_unionall_t *ub = (jl_unionall_t*)b;
if (tvar_names && ua->var->name != ub->var->name)
Expand All @@ -183,11 +183,11 @@ static int egal_types(const jl_value_t *a, const jl_value_t *b, jl_typeenv_t *en
jl_typeenv_t e = { ua->var, (jl_value_t*)ub->var, env };
return egal_types(ua->body, ub->body, &e, tvar_names);
}
if (dt == jl_uniontype_type) {
if (dtag == jl_uniontype_tag << 4) {
return egal_types(((jl_uniontype_t*)a)->a, ((jl_uniontype_t*)b)->a, env, tvar_names) &&
egal_types(((jl_uniontype_t*)a)->b, ((jl_uniontype_t*)b)->b, env, tvar_names);
}
if (dt == jl_vararg_type) {
if (dtag == jl_vararg_tag << 4) {
jl_vararg_t *vma = (jl_vararg_t*)a;
jl_vararg_t *vmb = (jl_vararg_t*)b;
jl_value_t *vmaT = vma->T ? vma->T : (jl_value_t*)jl_any_type;
Expand All @@ -198,10 +198,8 @@ static int egal_types(const jl_value_t *a, const jl_value_t *b, jl_typeenv_t *en
return egal_types(vma->N, vmb->N, env, tvar_names);
return !vma->N && !vmb->N;
}
if (dt == jl_symbol_type || dt == jl_module_type)
return 0;
assert(!dt->name->mutabl);
return jl_egal__bits(a, b, dt);
assert(dtag == jl_symbol_tag << 4 || dtag == jl_module_tag << 4 || !((jl_datatype_t*)jl_typeof(a))->name->mutabl);
return jl_egal__bitstag(a, b, dtag);
}

JL_DLLEXPORT int jl_types_egal(jl_value_t *a, jl_value_t *b)
Expand All @@ -215,45 +213,79 @@ JL_DLLEXPORT int (jl_egal)(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value
return jl_egal(a, b);
}

JL_DLLEXPORT int jl_egal__unboxed(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, jl_datatype_t *dt) JL_NOTSAFEPOINT
JL_DLLEXPORT int jl_egal__unboxed(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, uintptr_t dtag) JL_NOTSAFEPOINT
{
// warning: a,b may NOT have been gc-rooted by the caller
return jl_egal__unboxed_(a, b, dt);
}

int jl_egal__special(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, jl_datatype_t *dt) JL_NOTSAFEPOINT
{
if (dt == jl_simplevector_type)
return compare_svec((jl_svec_t*)a, (jl_svec_t*)b);
if (dt == jl_datatype_type) {
jl_datatype_t *dta = (jl_datatype_t*)a;
jl_datatype_t *dtb = (jl_datatype_t*)b;
if (dta->name != dtb->name)
return 0;
if (dta->name != jl_tuple_typename && (dta->isconcretetype || dtb->isconcretetype))
return 0;
return compare_svec(dta->parameters, dtb->parameters);
}
if (dt == jl_string_type) {
size_t l = jl_string_len(a);
if (jl_string_len(b) != l)
return jl_egal__unboxed_(a, b, dtag);
}

JL_DLLEXPORT int jl_egal__bitstag(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, uintptr_t dtag) JL_NOTSAFEPOINT
{
if (dtag < jl_max_tags << 4) {
switch ((enum jlsmall_typeof_tags)(dtag >> 4)) {
case jl_int8_tag:
case jl_uint8_tag:
return *(uint8_t*)a == *(uint8_t*)b;
case jl_int16_tag:
case jl_uint16_tag:
return *(uint16_t*)a == *(uint16_t*)b;
case jl_int32_tag:
case jl_uint32_tag:
case jl_char_tag:
return *(uint32_t*)a == *(uint32_t*)b;
case jl_int64_tag:
case jl_uint64_tag:
return *(uint64_t*)a == *(uint64_t*)b;
case jl_unionall_tag:
return egal_types(a, b, NULL, 1);
case jl_uniontype_tag:
return compare_fields(a, b, jl_uniontype_type);
case jl_vararg_tag:
return compare_fields(a, b, jl_vararg_type);
case jl_task_tag:
case jl_tvar_tag:
case jl_symbol_tag:
case jl_module_tag:
case jl_bool_tag:
return 0;
return !memcmp(jl_string_data(a), jl_string_data(b), l);
case jl_simplevector_tag:
return compare_svec((jl_svec_t*)a, (jl_svec_t*)b);
case jl_string_tag: {
size_t l = jl_string_len(a);
if (jl_string_len(b) != l)
return 0;
return !memcmp(jl_string_data(a), jl_string_data(b), l);
}
case jl_datatype_tag: {
jl_datatype_t *dta = (jl_datatype_t*)a;
jl_datatype_t *dtb = (jl_datatype_t*)b;
if (dta->name != dtb->name)
return 0;
if (dta->name != jl_tuple_typename && (dta->isconcretetype || dtb->isconcretetype))
return 0;
return compare_svec(dta->parameters, dtb->parameters);
}
#ifndef NDEBUG
default:
#endif
case jl_max_tags:
case jl_null_tag:
case jl_typeofbottom_tag:
case jl_tags_count:
abort();
}
}
assert(0 && "unreachable");
return 0;
return jl_egal__bits(a, b, (jl_datatype_t*)dtag);
}

int jl_egal__bits(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, jl_datatype_t *dt) JL_NOTSAFEPOINT
inline int jl_egal__bits(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value_t *b JL_MAYBE_UNROOTED, jl_datatype_t *dt) JL_NOTSAFEPOINT
{
size_t sz = jl_datatype_size(dt);
if (sz == 0)
return 1;
size_t nf = jl_datatype_nfields(dt);
if (nf == 0 || !dt->layout->haspadding)
return bits_equal(a, b, sz);
if (dt == jl_unionall_type)
return egal_types(a, b, NULL, 1);
return compare_fields(a, b, dt);
}

Expand Down Expand Up @@ -1417,6 +1449,7 @@ JL_DLLEXPORT jl_tvar_t *jl_new_typevar(jl_sym_t *name, jl_value_t *lb, jl_value_
jl_type_error_rt("TypeVar", "upper bound", (jl_value_t *)jl_type_type, ub);
jl_task_t *ct = jl_current_task;
jl_tvar_t *tv = (jl_tvar_t *)jl_gc_alloc(ct->ptls, sizeof(jl_tvar_t), jl_tvar_type);
jl_set_typetagof(tv, jl_tvar_tag, 0);
tv->name = name;
tv->lb = lb;
tv->ub = ub;
Expand Down Expand Up @@ -1650,7 +1683,7 @@ static int equiv_field_types(jl_value_t *old, jl_value_t *ft)
if (!jl_has_free_typevars(tb) || !jl_egal(ta, tb))
return 0;
}
else if (jl_has_free_typevars(tb) || jl_typeof(ta) != jl_typeof(tb) ||
else if (jl_has_free_typevars(tb) || jl_typetagof(ta) != jl_typetagof(tb) ||
!jl_types_equal(ta, tb)) {
return 0;
}
Expand Down Expand Up @@ -1763,7 +1796,7 @@ static int equiv_type(jl_value_t *ta, jl_value_t *tb)
if (!jl_is_datatype(dta))
return 0;
jl_datatype_t *dtb = (jl_datatype_t*)jl_unwrap_unionall(tb);
if (!(jl_typeof(dta) == jl_typeof(dtb) &&
if (!(jl_typetagof(dta) == jl_typetagof(dtb) &&
dta->name->name == dtb->name->name &&
dta->name->abstract == dtb->name->abstract &&
dta->name->mutabl == dtb->name->mutabl &&
Expand Down Expand Up @@ -1893,7 +1926,7 @@ static void add_intrinsic_properties(enum intrinsic f, unsigned nargs, void (*pf

static void add_intrinsic(jl_module_t *inm, const char *name, enum intrinsic f) JL_GC_DISABLED
{
jl_value_t *i = jl_permbox32(jl_intrinsic_type, (int32_t)f);
jl_value_t *i = jl_permbox32(jl_intrinsic_type, 0, (int32_t)f);
jl_sym_t *sym = jl_symbol(name);
jl_set_const(inm, sym, i);
jl_module_export(inm, sym);
Expand Down
Loading

0 comments on commit e26b63c

Please sign in to comment.