Skip to content

Commit

Permalink
Merge pull request #48693 from JuliaLang/avi/typedslot
Browse files Browse the repository at this point in the history
tidy up `Slot`s in the compiler
  • Loading branch information
aviatesk authored Feb 17, 2023
2 parents 8068e44 + 0f6f75a commit c110f7c
Show file tree
Hide file tree
Showing 25 changed files with 93 additions and 115 deletions.
12 changes: 6 additions & 6 deletions base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,6 @@ eval(Core, quote
LineInfoNode(mod::Module, @nospecialize(method), file::Symbol, line::Int32, inlined_at::Int32) =
$(Expr(:new, :LineInfoNode, :mod, :method, :file, :line, :inlined_at))
SlotNumber(n::Int) = $(Expr(:new, :SlotNumber, :n))
TypedSlot(n::Int, @nospecialize(t)) = $(Expr(:new, :TypedSlot, :n, :t))
PhiNode(edges::Array{Int32, 1}, values::Array{Any, 1}) = $(Expr(:new, :PhiNode, :edges, :values))
PiNode(@nospecialize(val), @nospecialize(typ)) = $(Expr(:new, :PiNode, :val, :typ))
PhiCNode(values::Array{Any, 1}) = $(Expr(:new, :PhiCNode, :values))
Expand Down Expand Up @@ -521,17 +520,18 @@ Symbol(s::Symbol) = s

# module providing the IR object model
module IR

export CodeInfo, MethodInstance, CodeInstance, GotoNode, GotoIfNot, ReturnNode,
NewvarNode, SSAValue, Slot, SlotNumber, TypedSlot, Argument,
NewvarNode, SSAValue, SlotNumber, Argument,
PiNode, PhiNode, PhiCNode, UpsilonNode, LineInfoNode,
Const, PartialStruct
Const, PartialStruct, InterConditional

import Core: CodeInfo, MethodInstance, CodeInstance, GotoNode, GotoIfNot, ReturnNode,
NewvarNode, SSAValue, Slot, SlotNumber, TypedSlot, Argument,
NewvarNode, SSAValue, SlotNumber, Argument,
PiNode, PhiNode, PhiCNode, UpsilonNode, LineInfoNode,
Const, PartialStruct
Const, PartialStruct, InterConditional

end
end # module IR

# docsystem basics
const unescape = Symbol("hygienic-scope")
Expand Down
5 changes: 2 additions & 3 deletions base/compiler/optimize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -242,13 +242,11 @@ function stmt_effect_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospe
isa(stmt, ReturnNode) && return (true, false, true)
isa(stmt, GotoNode) && return (true, false, true)
isa(stmt, GotoIfNot) && return (true, false, (𝕃ₒ, argextype(stmt.cond, src), Bool))
isa(stmt, Slot) && return (true, false, false) # Slots shouldn't occur in the IR at this point, but let's be defensive here
if isa(stmt, GlobalRef)
nothrow = isdefined(stmt.mod, stmt.name)
consistent = nothrow && isconst(stmt.mod, stmt.name)
return (consistent, nothrow, nothrow)
end
if isa(stmt, Expr)
elseif isa(stmt, Expr)
(; head, args) = stmt
if head === :static_parameter
# if we aren't certain enough about the type, it might be an UndefVarError at runtime
Expand Down Expand Up @@ -338,6 +336,7 @@ function stmt_effect_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospe
return (false, false, false)
end
end
isa(stmt, UnoptSlot) && error("unexpected IR elements")
return (true, true, true)
end

Expand Down
4 changes: 2 additions & 2 deletions base/compiler/ssair/passes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1708,8 +1708,8 @@ function type_lift_pass!(ir::IRCode)
# a phi node (or an UpsilonNode() argument to a PhiC node), so lift
# all these nodes that have maybe undef values
val = stmt.args[(stmt.head === :isdefined) ? 1 : 2]
if stmt.head === :isdefined && (val isa Slot || val isa GlobalRef ||
isexpr(val, :static_parameter) || val isa Argument || val isa Symbol)
if stmt.head === :isdefined && (val isa GlobalRef || isexpr(val, :static_parameter) ||
val isa Argument || val isa Symbol)
# this is a legal node, so assume it was not introduced by
# slot2ssa (at worst, we might leave in a runtime check that
# shouldn't have been there)
Expand Down
8 changes: 7 additions & 1 deletion base/compiler/ssair/slot2ssa.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

struct TypedSlot
id::Int
typ
TypedSlot(id::Int, @nospecialize(typ)) = new(id, typ)
end

const UnoptSlot = Union{SlotNumber, TypedSlot}

mutable struct SlotInfo
Expand Down Expand Up @@ -229,7 +235,7 @@ function typ_for_val(@nospecialize(x), ci::CodeInfo, sptypes::Vector{VarState},
isa(x, Argument) && return slottypes[x.n]
isa(x, NewSSAValue) && return DelayedTyp(x)
isa(x, QuoteNode) && return Const(x.value)
isa(x, Union{Symbol, PiNode, PhiNode, SlotNumber, TypedSlot}) && error("unexpected val type")
isa(x, Union{Symbol, PiNode, PhiNode, UnoptSlot}) && error("unexpected val type")
return Const(x)
end

Expand Down
2 changes: 1 addition & 1 deletion base/compiler/ssair/verify.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int,
elseif isa(op, Union{OldSSAValue, NewSSAValue})
@verify_error "Left over SSA marker"
error("")
elseif isa(op, Union{SlotNumber, TypedSlot})
elseif isa(op, UnoptSlot)
@verify_error "Left over slot detected in converted IR"
error("")
end
Expand Down
7 changes: 5 additions & 2 deletions base/compiler/utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -482,8 +482,11 @@ function find_throw_blocks(code::Vector{Any}, handler_at::Vector{Int})
end

# using a function to ensure we can infer this
@inline slot_id(s) = isa(s, SlotNumber) ? (s::SlotNumber).id :
isa(s, Argument) ? (s::Argument).n : (s::TypedSlot).id
@inline function slot_id(s)
isa(s, SlotNumber) && return s.id
isa(s, Argument) && return s.n
return (s::TypedSlot).id
end

###########
# options #
Expand Down
18 changes: 9 additions & 9 deletions base/compiler/validation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,11 @@ function _validate_val!(@nospecialize(x), errors, ssavals::BitSet)
end

"""
validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo)
validate_code!(errors::Vector{InvalidCodeError}, c::CodeInfo)
Validate `c`, logging any violation by pushing an `InvalidCodeError` into `errors`.
"""
function validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo, is_top_level::Bool = false)
function validate_code!(errors::Vector{InvalidCodeError}, c::CodeInfo, is_top_level::Bool = false)
ssavals = BitSet()
lhs_slotnums = BitSet()

Expand Down Expand Up @@ -199,15 +199,15 @@ function validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo, is_top_
end

"""
validate_code!(errors::Vector{>:InvalidCodeError}, mi::MethodInstance,
validate_code!(errors::Vector{InvalidCodeError}, mi::MethodInstance,
c::Union{Nothing,CodeInfo} = Core.Compiler.retrieve_code_info(mi))
Validate `mi`, logging any violation by pushing an `InvalidCodeError` into `errors`.
If `isa(c, CodeInfo)`, also call `validate_code!(errors, c)`. It is assumed that `c` is
the `CodeInfo` instance associated with `mi`.
"""
function validate_code!(errors::Vector{>:InvalidCodeError}, mi::Core.MethodInstance,
function validate_code!(errors::Vector{InvalidCodeError}, mi::Core.MethodInstance,
c::Union{Nothing,CodeInfo} = Core.Compiler.retrieve_code_info(mi))
is_top_level = mi.def isa Module
if is_top_level
Expand All @@ -231,13 +231,13 @@ end

validate_code(args...) = validate_code!(Vector{InvalidCodeError}(), args...)

is_valid_lvalue(@nospecialize(x)) = isa(x, Slot) || isa(x, GlobalRef)
is_valid_lvalue(@nospecialize(x)) = isa(x, UnoptSlot) || isa(x, GlobalRef)

function is_valid_argument(@nospecialize(x))
if isa(x, Slot) || isa(x, Argument) || isa(x, SSAValue) || isa(x, GlobalRef) || isa(x, QuoteNode) ||
(isa(x,Expr) && (x.head in (:static_parameter, :boundscheck))) ||
isa(x, Number) || isa(x, AbstractString) || isa(x, AbstractChar) || isa(x, Tuple) ||
isa(x, Type) || isa(x, Core.Box) || isa(x, Module) || x === nothing
if isa(x, UnoptSlot) || isa(x, Argument) || isa(x, SSAValue) ||
isa(x, GlobalRef) || isa(x, QuoteNode) || isexpr(x, (:static_parameter, :boundscheck)) ||
isa(x, Number) || isa(x, AbstractString) || isa(x, AbstractChar) || isa(x, Tuple) ||
isa(x, Type) || isa(x, Core.Box) || isa(x, Module) || x === nothing
return true
end
# TODO: consider being stricter about what needs to be wrapped with QuoteNode
Expand Down
12 changes: 7 additions & 5 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1372,9 +1372,11 @@ show(io::IO, s::Symbol) = show_unquoted_quote_expr(io, s, 0, 0, 0)
# eval(Meta.parse("Set{Int64}([2,3,1])")) # ==> An actual set
# While this isn’t true of ALL show methods, it is of all ASTs.

const ExprNode = Union{Expr, QuoteNode, Slot, LineNumberNode, SSAValue,
GotoNode, GlobalRef, PhiNode, PhiCNode, UpsilonNode,
Core.Compiler.GotoIfNot, Core.Compiler.ReturnNode}
using Core.Compiler: TypedSlot, UnoptSlot

const ExprNode = Union{Expr, QuoteNode, UnoptSlot, LineNumberNode, SSAValue,
GotoNode, GotoIfNot, GlobalRef, PhiNode, PhiCNode, UpsilonNode,
ReturnNode}
# Operators have precedence levels from 1-N, and show_unquoted defaults to a
# precedence level of 0 (the fourth argument). The top-level print and show
# methods use a precedence of -1 to specially allow space-separated macro syntax.
Expand Down Expand Up @@ -1723,7 +1725,7 @@ function show_globalref(io::IO, ex::GlobalRef; allow_macroname=false)
nothing
end

function show_unquoted(io::IO, ex::Slot, ::Int, ::Int)
function show_unquoted(io::IO, ex::UnoptSlot, ::Int, ::Int)
typ = isa(ex, TypedSlot) ? ex.typ : Any
slotid = ex.id
slotnames = get(io, :SOURCE_SLOTNAMES, false)
Expand Down Expand Up @@ -2588,7 +2590,7 @@ module IRShow
const Compiler = Core.Compiler
using Core.IR
import ..Base
import .Compiler: IRCode, ReturnNode, GotoIfNot, CFG, scan_ssa_use!, Argument,
import .Compiler: IRCode, TypedSlot, CFG, scan_ssa_use!,
isexpr, compute_basic_blocks, block_for_inst, IncrementalCompact,
Effects, ALWAYS_TRUE, ALWAYS_FALSE
Base.getindex(r::Compiler.StmtRange, ind::Integer) = Compiler.getindex(r, ind)
Expand Down
18 changes: 10 additions & 8 deletions doc/src/devdocs/ast.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,14 +249,16 @@ types exist in lowered form:
While almost every part of a surface AST is represented by an `Expr`, the IR uses only a
limited number of `Expr`s, mostly for calls and some top-level-only forms.

* `Slot`
* `SlotNumber`

Identifies arguments and local variables by consecutive numbering. `Slot` is an abstract type
with subtypes `SlotNumber` and `TypedSlot`. Both types have an integer-valued `id` field giving
the slot index. Most slots have the same type at all uses, and so are represented with `SlotNumber`.
The types of these slots are found in the `slottypes` field of their `CodeInfo` object.
Slots that require per-use type annotations are represented with `TypedSlot`, which has a `typ`
field.
Identifies arguments and local variables by consecutive numbering. It has an
integer-valued `id` field giving the slot index.
The types of these slots can be found in the `slottypes` field of their `CodeInfo` object.
When a slot has different types at different uses and thus requires per-use type annotations,
they are converted to temporary `Core.Compiler.TypedSlot` object. This object has an
additional `typ` field as well as the `id` field. Note that `Core.Compiler.TypedSlot`
only appears in an unoptimized lowered form that is scheduled for optimization,
and it never appears elsewhere.

* `Argument`

Expand Down Expand Up @@ -322,7 +324,7 @@ These symbols appear in the `head` field of [`Expr`](@ref)s in lowered form.

* `=`

Assignment. In the IR, the first argument is always a Slot or a GlobalRef.
Assignment. In the IR, the first argument is always a `SlotNumber` or a `GlobalRef`.

* `method`

Expand Down
4 changes: 2 additions & 2 deletions src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -686,8 +686,8 @@ static value_t julia_to_scm_noalloc2(fl_context_t *fl_ctx, jl_value_t *v, int ch
if (check_valid) {
if (jl_is_ssavalue(v))
lerror(fl_ctx, symbol(fl_ctx, "error"), "SSAValue objects should not occur in an AST");
if (jl_is_slot(v))
lerror(fl_ctx, symbol(fl_ctx, "error"), "Slot objects should not occur in an AST");
if (jl_is_slotnumber(v))
lerror(fl_ctx, symbol(fl_ctx, "error"), "SlotNumber objects should not occur in an AST");
}
value_t opaque = cvalue(fl_ctx, jl_ast_ctx(fl_ctx)->jvtype, sizeof(void*));
*(jl_value_t**)cv_data((cvalue_t*)ptr(opaque)) = v;
Expand Down
2 changes: 0 additions & 2 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -2029,9 +2029,7 @@ void jl_init_primitives(void) JL_GC_DISABLED
add_builtin("TypeMapLevel", (jl_value_t*)jl_typemap_level_type);
add_builtin("Symbol", (jl_value_t*)jl_symbol_type);
add_builtin("SSAValue", (jl_value_t*)jl_ssavalue_type);
add_builtin("Slot", (jl_value_t*)jl_abstractslot_type);
add_builtin("SlotNumber", (jl_value_t*)jl_slotnumber_type);
add_builtin("TypedSlot", (jl_value_t*)jl_typedslot_type);
add_builtin("Argument", (jl_value_t*)jl_argument_type);
add_builtin("Const", (jl_value_t*)jl_const_type);
add_builtin("PartialStruct", (jl_value_t*)jl_partial_struct_type);
Expand Down
35 changes: 14 additions & 21 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2500,7 +2500,7 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex)
return jl_get_global(ctx.module, sym);
return NULL;
}
if (jl_is_slot(ex) || jl_is_argument(ex))
if (jl_is_slotnumber(ex) || jl_is_argument(ex))
return NULL;
if (jl_is_ssavalue(ex)) {
ssize_t idx = ((jl_ssavalue_t*)ex)->id - 1;
Expand Down Expand Up @@ -2594,7 +2594,7 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex)

static bool slot_eq(jl_value_t *e, int sl)
{
return (jl_is_slot(e) || jl_is_argument(e)) && jl_slot_number(e)-1 == sl;
return (jl_is_slotnumber(e) || jl_is_argument(e)) && jl_slot_number(e)-1 == sl;
}

// --- code gen for intrinsic functions ---
Expand Down Expand Up @@ -2637,7 +2637,7 @@ static std::set<int> assigned_in_try(jl_array_t *stmts, int s, long l)
if (jl_is_expr(st)) {
if (((jl_expr_t*)st)->head == jl_assign_sym) {
jl_value_t *ar = jl_exprarg(st, 0);
if (jl_is_slot(ar)) {
if (jl_is_slotnumber(ar)) {
av.insert(jl_slot_number(ar)-1);
}
}
Expand Down Expand Up @@ -2740,7 +2740,7 @@ static void general_use_analysis(jl_codectx_t &ctx, jl_value_t *expr, callback &
static void simple_use_analysis(jl_codectx_t &ctx, jl_value_t *expr)
{
auto scan_slot_arg = [&](jl_value_t *expr) {
if (jl_is_slot(expr) || jl_is_argument(expr)) {
if (jl_is_slotnumber(expr) || jl_is_argument(expr)) {
int i = jl_slot_number(expr) - 1;
ctx.slots[i].used = true;
return true;
Expand Down Expand Up @@ -4517,7 +4517,7 @@ static jl_cgval_t emit_sparam(jl_codectx_t &ctx, size_t i)
static jl_cgval_t emit_isdefined(jl_codectx_t &ctx, jl_value_t *sym)
{
Value *isnull = NULL;
if (jl_is_slot(sym) || jl_is_argument(sym)) {
if (jl_is_slotnumber(sym) || jl_is_argument(sym)) {
size_t sl = jl_slot_number(sym) - 1;
jl_varinfo_t &vi = ctx.slots[sl];
if (!vi.usedUndef)
Expand Down Expand Up @@ -4597,8 +4597,8 @@ static jl_cgval_t emit_isdefined(jl_codectx_t &ctx, jl_value_t *sym)
return mark_julia_type(ctx, isnull, false, jl_bool_type);
}

static jl_cgval_t emit_varinfo(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_sym_t *varname, jl_value_t *better_typ=NULL) {
jl_value_t *typ = better_typ ? better_typ : vi.value.typ;
static jl_cgval_t emit_varinfo(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_sym_t *varname) {
jl_value_t *typ = vi.value.typ;
jl_cgval_t v;
Value *isnull = NULL;
if (vi.boxroot == NULL || vi.pTIndex != NULL) {
Expand Down Expand Up @@ -4675,14 +4675,7 @@ static jl_cgval_t emit_local(jl_codectx_t &ctx, jl_value_t *slotload)
size_t sl = jl_slot_number(slotload) - 1;
jl_varinfo_t &vi = ctx.slots[sl];
jl_sym_t *sym = slot_symbol(ctx, sl);
jl_value_t *typ = NULL;
if (jl_typeis(slotload, jl_typedslot_type)) {
// use the better type from inference for this load
typ = jl_typedslot_get_type(slotload);
if (jl_is_typevar(typ))
typ = ((jl_tvar_t*)typ)->ub;
}
return emit_varinfo(ctx, vi, sym, typ);
return emit_varinfo(ctx, vi, sym);
}

static void emit_vi_assignment_unboxed(jl_codectx_t &ctx, jl_varinfo_t &vi, Value *isboxed, jl_cgval_t rval_info)
Expand Down Expand Up @@ -4929,7 +4922,7 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r, ssi
assert(!jl_is_ssavalue(l));
jl_cgval_t rval_info = emit_expr(ctx, r, ssaval);

if (jl_is_slot(l)) {
if (jl_is_slotnumber(l)) {
int sl = jl_slot_number(l) - 1;
// it's a local variable
jl_varinfo_t &vi = ctx.slots[sl];
Expand Down Expand Up @@ -5035,7 +5028,7 @@ static void emit_stmtpos(jl_codectx_t &ctx, jl_value_t *expr, int ssaval_result)
{
if (jl_is_ssavalue(expr) && ssaval_result == -1)
return; // value not used, no point in attempting codegen for it
if (jl_is_slot(expr) && ssaval_result == -1) {
if (jl_is_slotnumber(expr) && ssaval_result == -1) {
size_t sl = jl_slot_number(expr) - 1;
jl_varinfo_t &vi = ctx.slots[sl];
if (vi.usedUndef)
Expand All @@ -5047,7 +5040,7 @@ static void emit_stmtpos(jl_codectx_t &ctx, jl_value_t *expr, int ssaval_result)
}
if (jl_is_newvarnode(expr)) {
jl_value_t *var = jl_fieldref(expr, 0);
assert(jl_is_slot(var));
assert(jl_is_slotnumber(var));
jl_varinfo_t &vi = ctx.slots[jl_slot_number(var)-1];
if (vi.usedUndef) {
// create a new uninitialized variable
Expand Down Expand Up @@ -5169,7 +5162,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_
jl_sym_t *sym = (jl_sym_t*)expr;
return emit_globalref(ctx, ctx.module, sym, AtomicOrdering::Unordered);
}
if (jl_is_slot(expr) || jl_is_argument(expr)) {
if (jl_is_slotnumber(expr) || jl_is_argument(expr)) {
return emit_local(ctx, expr);
}
if (jl_is_ssavalue(expr)) {
Expand Down Expand Up @@ -5286,7 +5279,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_
else if (head == jl_method_sym) {
if (nargs == 1) {
jl_value_t *mn = args[0];
assert(jl_is_symbol(mn) || jl_is_slot(mn));
assert(jl_is_symbol(mn) || jl_is_slotnumber(mn));

Value *bp = NULL, *name;
jl_binding_t *bnd = NULL;
Expand Down Expand Up @@ -5314,7 +5307,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_
bp = julia_binding_gv(ctx, bnd);
bp = julia_binding_pvalue(ctx, bp);
}
else if (jl_is_slot(mn) || jl_is_argument(mn)) {
else if (jl_is_slotnumber(mn) || jl_is_argument(mn)) {
// XXX: eval_methoddef does not have this code branch
int sl = jl_slot_number(mn)-1;
jl_varinfo_t &vi = ctx.slots[sl];
Expand Down
Loading

0 comments on commit c110f7c

Please sign in to comment.