Skip to content

Commit

Permalink
Refactor class var initialization (#8091)
Browse files Browse the repository at this point in the history
* Refactor class var initialization

* Simple class vars are not eager initialized anymore

* Simplify code of class var codegen: pass around the MetaTypeVar as much as possible instead of its members

* Cache initialization of simple class vars
  • Loading branch information
waj committed Aug 17, 2019
1 parent b42c5dd commit c45abe2
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 165 deletions.
246 changes: 131 additions & 115 deletions src/compiler/crystal/codegen/class_var.cr

Large diffs are not rendered by default.

50 changes: 11 additions & 39 deletions src/compiler/crystal/codegen/codegen.cr
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ module Crystal

initialize_argv_and_argc

initialize_simple_class_vars_and_constants
initialize_simple_constants

if @debug.line_numbers? && (filename = @program.filename)
set_current_debug_location Location.new(filename, 1, 1)
Expand All @@ -261,39 +261,15 @@ module Crystal
wrap_builder(llvm_context.new_builder)
end

# Here we only initialize simple constants and class variables, those
# Here we only initialize simple constants, those
# that has simple values like 1, "foo" and other literals.
def initialize_simple_class_vars_and_constants
@program.class_var_and_const_initializers.each do |initializer|
case initializer
when Const
# Simple constants are never initialized: they are always inlined
next if initializer.compile_time_value
next unless initializer.simple?

initialize_simple_const(initializer)
when ClassVarInitializer
next unless initializer.node.simple_literal?

owner = initializer.owner
class_var = owner.class_vars[initializer.name]
next if class_var.thread_local?

initialize_simple_class_var(owner, class_var, initializer)
owner.all_subclasses.each do |subclass|
if subclass.is_a?(ClassVarContainer)
initialize_simple_class_var(subclass, class_var, initializer)
end
end
def initialize_simple_constants
@program.const_initializers.each do |initializer|
# Simple constants are never initialized: they are always inlined
next if initializer.compile_time_value
next unless initializer.simple?

if owner.responds_to?(:raw_including_types) && (including_types = owner.raw_including_types)
including_types.each do |type|
if type.is_a?(ClassVarContainer)
initialize_simple_class_var(type, class_var, initializer)
end
end
end
end
initialize_simple_const(initializer)
end
end

Expand Down Expand Up @@ -527,7 +503,7 @@ module Crystal
if node_exp.var.initializer
initialize_class_var(node_exp)
end
get_global class_var_global_name(node_exp.var.owner, node_exp.var.name), node_exp.type, node_exp.var
get_global class_var_global_name(node_exp.var), node_exp.type, node_exp.var
when Global
get_global node_exp.name, node_exp.type, node_exp.var
when Path
Expand Down Expand Up @@ -964,12 +940,8 @@ module Crystal
# or a class variable initializer
unless target_type
if target.is_a?(ClassVar)
class_var = target.var.initializer.try(&.owner.lookup_class_var(target.name))

if !class_var || class_var.thread_local? || !value.simple_literal?
# This is the case of a class var initializer
initialize_class_var(target)
end
# This is the case of a class var initializer
initialize_class_var(target)
end
return false
end
Expand Down
11 changes: 7 additions & 4 deletions src/compiler/crystal/program.cr
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,13 @@ module Crystal
# The cache directory where temporary files are placed.
setter cache_dir : String?

# Here we store class var initializers and constants, in the
# Here we store constants, in the
# order that they are used. They will be initialized as soon
# as the program starts, before the main code.
getter class_var_and_const_initializers = [] of ClassVarInitializer | Const
getter const_initializers = [] of Const

# The class var initializers stored to be used by the cleanup transformer
getter class_var_initializers = [] of ClassVarInitializer

# The constant for ARGC_UNSAFE
getter! argc : Const
Expand Down Expand Up @@ -219,8 +222,8 @@ module Crystal
types["ARGV_UNSAFE"] = @argv = argv_unsafe = Const.new self, self, "ARGV_UNSAFE", Primitive.new("argv", pointer_of(pointer_of(uint8)))

# Make sure to initialize `ARGC_UNSAFE` and `ARGV_UNSAFE` as soon as the program starts
class_var_and_const_initializers << argc_unsafe
class_var_and_const_initializers << argv_unsafe
const_initializers << argc_unsafe
const_initializers << argv_unsafe

types["GC"] = gc = NonGenericModuleType.new self, self, "GC"
gc.metaclass.as(ModuleType).add_def Def.new("add_finalizer", [Arg.new("object")], Nop.new)
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/crystal/semantic/ast.cr
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,10 @@ module Crystal
# The (optional) initial value of a class variable
property initializer : ClassVarInitializer?

# Flag used during codegen to indicate the initializer is simple
# and doesn't require a call to a function
property? simple_initializer = false

# Is this variable thread local? Only applicable
# to global and class variables.
property? thread_local = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ module Crystal

class_var.bind_to(node)
class_var.initializer = initializer
self.class_var_and_const_initializers << initializer
self.class_var_initializers << initializer
end

node
Expand Down
6 changes: 2 additions & 4 deletions src/compiler/crystal/semantic/cleanup_transformer.cr
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ module Crystal
cleanup_type type, transformer
end

self.class_var_and_const_initializers.each do |initializer|
if initializer.is_a?(ClassVarInitializer)
initializer.node = initializer.node.transform(transformer)
end
self.class_var_initializers.each do |initializer|
initializer.node = initializer.node.transform(transformer)
end
end

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/crystal/semantic/main_visitor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ module Crystal
type.visitor = self
type.used = true

program.class_var_and_const_initializers << type
program.const_initializers << type
end

node.target_const = type
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/crystal/types.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2681,7 +2681,7 @@ module Crystal

def add_constant(constant)
types[constant.name] = const = Const.new(program, self, constant.name, constant.default_value.not_nil!)
program.class_var_and_const_initializers << const
program.const_initializers << const
const
end

Expand Down
4 changes: 4 additions & 0 deletions src/llvm/function.cr
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,8 @@ struct LLVM::Function
def personality_function=(fn)
LibLLVM.set_personality_fn(self, fn)
end

def delete
LibLLVM.delete_function(self)
end
end
1 change: 1 addition & 0 deletions src/llvm/lib_llvm.cr
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ lib LibLLVM
fun create_mc_jit_compiler_for_module = LLVMCreateMCJITCompilerForModule(jit : ExecutionEngineRef*, m : ModuleRef, options : JITCompilerOptions*, options_length : UInt32, error : UInt8**) : Int32
fun create_target_machine = LLVMCreateTargetMachine(target : TargetRef, triple : UInt8*, cpu : UInt8*, features : UInt8*, level : LLVM::CodeGenOptLevel, reloc : LLVM::RelocMode, code_model : LLVM::CodeModel) : TargetMachineRef
fun delete_basic_block = LLVMDeleteBasicBlock(block : BasicBlockRef)
fun delete_function = LLVMDeleteFunction(fn : ValueRef)
fun dispose_message = LLVMDisposeMessage(msg : UInt8*)
fun dump_module = LLVMDumpModule(module : ModuleRef)
fun dump_value = LLVMDumpValue(val : ValueRef)
Expand Down

0 comments on commit c45abe2

Please sign in to comment.