diff --git a/lib/crystal/codegen.rb b/lib/crystal/codegen.rb index 1fc202d99072bdcde6c62ffd83520ef28208d979..95fb1f8d131d4502b3503d8edc9311b3c8cc7136 100644 --- a/lib/crystal/codegen.rb +++ b/lib/crystal/codegen.rb @@ -256,7 +256,7 @@ module Crystal end def visit_fun_call(node) - @last = @builder.call @fun.params[0], *@fun.params.to_a[1 .. -1] + @last = @builder.call @call_args[0], *@call_args[1 .. -1] end def visit_external_var(node) @@ -609,11 +609,11 @@ module Crystal def visit_pointer_malloc(node) llvm_type = llvm_embedded_type(node.type.var.type) - @last = @builder.array_malloc(llvm_type, @vars['size'][:ptr]) + @last = @builder.array_malloc(llvm_type, @call_args[1]) end def visit_pointer_new(node) - @last = @builder.int2ptr(@vars["address"][:ptr], llvm_type(node.type)) + @last = @builder.int2ptr(@call_args[1], llvm_type(node.type)) end def visit_pointer_null(node) @@ -621,8 +621,8 @@ module Crystal end def visit_pointer_realloc(node) - casted_ptr = cast_to_void_pointer(llvm_self) - size = @vars['size'][:ptr] + casted_ptr = cast_to_void_pointer(@call_args[0]) + size = @call_args[1] size = @builder.mul size, LLVM::Int64.from_i(@type.var.type.llvm_size) reallocated_ptr = realloc casted_ptr, size @last = cast_to_pointer reallocated_ptr, @type.var.type @@ -630,46 +630,46 @@ module Crystal def visit_pointer_get_value(node) if @type.var.type.union? || @type.var.type.c_struct? || @type.var.type.c_union? - @last = llvm_self + @last = @call_args[0] else - @last = @builder.load llvm_self + @last = @builder.load @call_args[0] end end def visit_pointer_set_value(node) - value = @fun.params[1] + value = @call_args[1] if node.type.c_struct? || node.type.c_union? loaded_value = @builder.load value - @builder.store loaded_value, @fun.params[0] + @builder.store loaded_value, @call_args[0] @last = value return end if node.type.union? value = @builder.alloca llvm_type(node.type) - target = @fun.params[1] + target = @call_args[1] target = @builder.load(target) if node.type.passed_by_val? @builder.store target, value end - codegen_assign llvm_self, @type.var.type, node.type, value + codegen_assign @call_args[0], @type.var.type, node.type, value @last = value end def visit_pointer_add(node) - @last = gep(llvm_self, @fun.params[1]) + @last = gep(@call_args[0], @call_args[1]) end def visit_pointer_diff(node) - p0 = @builder.ptr2int(@fun.params[0], LLVM::UInt64) - p1 = @builder.ptr2int(@fun.params[1], LLVM::UInt64) + p0 = @builder.ptr2int(@call_args[0], LLVM::UInt64) + p1 = @builder.ptr2int(@call_args[1], LLVM::UInt64) sub = @builder.sub p0, p1 - @last = @builder.exact_sdiv sub, @builder.ptr2int(gep(llvm_self.type.null, LLVM::Int(1)), LLVM::UInt64) + @last = @builder.exact_sdiv sub, @builder.ptr2int(gep(@call_args[0].type.null, LLVM::Int(1)), LLVM::UInt64) end def visit_pointer_cast(node) - @last = cast_to @fun.params[0], node.type + @last = cast_to @call_args[0], node.type end def visit_simple_or(node) @@ -849,7 +849,7 @@ module Crystal end def visit_primitive_body(node) - @last = node.block.call(@builder, @fun, @llvm_mod, @type) + @last = node.block.call(@builder, @call_args, @llvm_mod, @type) end def visit_allocate(node) @@ -875,16 +875,16 @@ module Crystal var = @type.vars[node.name.to_s] index = @type.index_of_var(node.name) if var.type.c_struct? || var.type.c_union? - @last = gep llvm_self, 0, index + @last = gep @call_args[0], 0, index else - struct = @builder.load llvm_self + struct = @builder.load @call_args[0] @last = @builder.extract_value struct, index, node.name end end def visit_struct_set(node) - ptr = gep llvm_self, 0, @type.index_of_var(node.name) - @last = @vars['value'][:ptr] + ptr = gep @call_args[0], 0, @type.index_of_var(node.name) + @last = @call_args[1] value = @last value = @builder.load @last if node.type.c_struct? || node.type.c_union? @builder.store value, ptr @@ -897,8 +897,8 @@ module Crystal def visit_lib_set(node) var = declare_lib_var node - @builder.store @fun.params[0], var - @last = @fun.params[0] + @builder.store @call_args[0], var + @last = @call_args[0] end def declare_lib_var(node) @@ -919,7 +919,7 @@ module Crystal def visit_union_get(node) var = @type.vars[node.name.to_s] - ptr = gep llvm_self, 0, 0 + ptr = gep @call_args[0], 0, 0 if var.type.c_struct? || var.type.c_union? @last = @builder.bit_cast(ptr, LLVM::Pointer(llvm_struct_type(var.type))) else @@ -930,9 +930,9 @@ module Crystal def visit_union_set(node) var = @type.vars[node.name.to_s] - ptr = gep llvm_self, 0, 0 + ptr = gep @call_args[0], 0, 0 casted_value = cast_to_pointer ptr, var.type - @last = @vars['value'][:ptr] + @last = @call_args[1] @builder.store @last, casted_value end @@ -1312,6 +1312,19 @@ module Crystal def codegen_call(node, self_type, call_args) target_def = node.target_def + + if target_def.body.is_a?(Primitive) + old_type = @type + @type = self_type + @call_args = call_args + + target_def.body.accept self + + @type = old_type + + return + end + fun = target_def_fun(target_def, self_type) # Check for struct out arguments: alloca before the call, then copy to the pointer value after the call. diff --git a/lib/crystal/primitives.rb b/lib/crystal/primitives.rb index e67ce3b3f078b776143e37eecd87364bececc382..08cee9905602365e276466c1964069008b8b6ccf 100644 --- a/lib/crystal/primitives.rb +++ b/lib/crystal/primitives.rb @@ -17,7 +17,7 @@ module Crystal define_number_operations define_math_primitives - singleton(self, 'debugger', {}, void) do |b, f, llvm_mod| + singleton(self, 'debugger', {}, void) do |b, args, llvm_mod| int3_type = LLVM::Type.function([], LLVM.Void) b.call LLVM::C.const_inline_asm int3_type, "int3", "", 1, 0 end @@ -29,29 +29,29 @@ module Crystal end def define_reference_primitives - a_def = no_args_primitive(reference, 'object_id', uint64) do |b, f, llvm_mod, self_type| + a_def = no_args_primitive(reference, 'object_id', uint64) do |b, args, llvm_mod, self_type| if self_type.hierarchy? - obj = b.load(b.gep(f.params[0], [LLVM::Int(0), LLVM::Int(1)])) + obj = b.load(b.gep(args[0], [LLVM::Int(0), LLVM::Int(1)])) b.ptr2int(obj, LLVM::UInt64) else - b.ptr2int(f.params[0], LLVM::UInt64) + b.ptr2int(args[0], LLVM::UInt64) end end - a_def = no_args_primitive(reference, 'crystal_type_id', int32) do |b, f, llvm_mod, self_type| + a_def = no_args_primitive(reference, 'crystal_type_id', int32) do |b, args, llvm_mod, self_type| if self_type.hierarchy? - id = b.load(b.gep(f.params[0], [LLVM::Int(0), LLVM::Int(0)])) + id = b.load(b.gep(args[0], [LLVM::Int(0), LLVM::Int(0)])) LLVM::Int(id) else LLVM::Int(self_type.type_id) end end - a_def = no_args_primitive(reference, 'to_cstr', char_pointer) do |b, f, llvm_mod, self_type| + a_def = no_args_primitive(reference, 'to_cstr', char_pointer) do |b, args, llvm_mod, self_type| if self_type.hierarchy? - obj = b.load(b.gep(f.params[0], [LLVM::Int(0), LLVM::Int(1)])) + obj = b.load(b.gep(args[0], [LLVM::Int(0), LLVM::Int(1)])) else - obj = f.params[0] + obj = args[0] end buffer = b.array_malloc(LLVM::Int8, LLVM::Int(self_type.to_s.length + 23)) b.call sprintf(llvm_mod), buffer, b.global_string_pointer("#<#{self_type.to_s}:0x%016lx>"), obj @@ -61,30 +61,30 @@ module Crystal def define_value_primitives [value, bool, char, int32, int64, float32, float64, symbol].each do |klass| - no_args_primitive(klass, 'nil?', bool) { |b, f| LLVM::Int1.from_i(0) } - no_args_primitive(klass, 'crystal_type_id', int32) { |b, f, llvm_mod, self_type| LLVM::Int(self_type.type_id) } + no_args_primitive(klass, 'nil?', bool) { |b, args| LLVM::Int1.from_i(0) } + no_args_primitive(klass, 'crystal_type_id', int32) { |b, args, llvm_mod, self_type| LLVM::Int(self_type.type_id) } end end def define_bool_primitives - singleton(bool, :==, {'other' => bool}, bool) { |b, f| b.icmp(:eq, f.params[0], f.params[1]) } - singleton(bool, :'!@', {}, bool) { |b, f| b.not(f.params[0]) } + singleton(bool, :==, {'other' => bool}, bool) { |b, args| b.icmp(:eq, args[0], args[1]) } + singleton(bool, :'!@', {}, bool) { |b, args| b.not(args[0]) } end def define_char_primitives - no_args_primitive(char, 'ord', int32) { |b, f| b.zext(f.params[0], int32.llvm_type) } - singleton(char, :==, {'other' => char}, bool) { |b, f| b.icmp(:eq, f.params[0], f.params[1]) } - singleton(char, :'!=', {'other' => char}, bool) { |b, f| b.icmp(:ne, f.params[0], f.params[1]) } - singleton(char, :<, {'other' => char}, bool) { |b, f| b.icmp(:ult, f.params[0], f.params[1]) } - singleton(char, :<=, {'other' => char}, bool) { |b, f| b.icmp(:ule, f.params[0], f.params[1]) } - singleton(char, :>, {'other' => char}, bool) { |b, f| b.icmp(:ugt, f.params[0], f.params[1]) } - singleton(char, :>=, {'other' => char}, bool) { |b, f| b.icmp(:uge, f.params[0], f.params[1]) } + no_args_primitive(char, 'ord', int32) { |b, args| b.zext(args[0], int32.llvm_type) } + singleton(char, :==, {'other' => char}, bool) { |b, args| b.icmp(:eq, args[0], args[1]) } + singleton(char, :'!=', {'other' => char}, bool) { |b, args| b.icmp(:ne, args[0], args[1]) } + singleton(char, :<, {'other' => char}, bool) { |b, args| b.icmp(:ult, args[0], args[1]) } + singleton(char, :<=, {'other' => char}, bool) { |b, args| b.icmp(:ule, args[0], args[1]) } + singleton(char, :>, {'other' => char}, bool) { |b, args| b.icmp(:ugt, args[0], args[1]) } + singleton(char, :>=, {'other' => char}, bool) { |b, args| b.icmp(:uge, args[0], args[1]) } end def define_math_primitives math = types['Math'].metaclass - singleton(math, 'sqrt', {'other' => float32}, float32) { |b, f, llvm_mod| b.call(sqrtf(llvm_mod), f.params[1]) } - singleton(math, 'sqrt', {'other' => float64}, float64) { |b, f, llvm_mod| b.call(sqrt(llvm_mod), f.params[1]) } + singleton(math, 'sqrt', {'other' => float32}, float32) { |b, args, llvm_mod| b.call(sqrtf(llvm_mod), args[1]) } + singleton(math, 'sqrt', {'other' => float64}, float64) { |b, args, llvm_mod| b.call(sqrt(llvm_mod), args[1]) } end INT_CALC_OP_MAP = { :+ => :add, :- => :sub, :* => :mul, :/ => :sdiv, :% => :srem, :<< => :shl, :>> => :ashr, :| => :or, :& => :and, :"^" => :xor } @@ -177,15 +177,15 @@ module Crystal [:+, :-, :*, :/].each do |op| ret_type = greatest_type(type1, type2) if ret_type.equal?(float32) || ret_type.equal?(float64) - singleton(type1, op, {'other' => type2}, ret_type) do |b, f| - arg1 = adjust_calc_type(b, ret_type, type1, f.params[0]) - arg2 = adjust_calc_type(b, ret_type, type2, f.params[1]) + singleton(type1, op, {'other' => type2}, ret_type) do |b, args| + arg1 = adjust_calc_type(b, ret_type, type1, args[0]) + arg2 = adjust_calc_type(b, ret_type, type2, args[1]) build_calc_op(b, ret_type, op, arg1, arg2) end else - singleton(type1, op, {'other' => type2}, type1) do |b, f| - arg1 = adjust_calc_type(b, ret_type, type1, f.params[0]) - arg2 = adjust_calc_type(b, ret_type, type2, f.params[1]) + singleton(type1, op, {'other' => type2}, type1) do |b, args| + arg1 = adjust_calc_type(b, ret_type, type1, args[0]) + arg2 = adjust_calc_type(b, ret_type, type2, args[1]) ret = build_calc_op(b, ret_type, op, arg1, arg2) cast_back(b, type1, ret_type, ret) end @@ -194,48 +194,48 @@ module Crystal [:==, :>, :>=, :<, :<=, :!=].each do |op| comp_type = greatest_type(type1, type2) - singleton(type1, op, {'other' => type2}, bool) do |b, f| - arg1 = adjust_calc_type(b, comp_type, type1, f.params[0]) - arg2 = adjust_calc_type(b, comp_type, type2, f.params[1]) + singleton(type1, op, {'other' => type2}, bool) do |b, args| + arg1 = adjust_calc_type(b, comp_type, type1, args[0]) + arg2 = adjust_calc_type(b, comp_type, type2, args[1]) build_comp_op(b, comp_type, op, arg1, arg2) end end - no_args_primitive(type1, "to_#{type2.suffix}", type2) do |b, f| - adjust_calc_type(b, type2, type1, f.params[0]) + no_args_primitive(type1, "to_#{type2.suffix}", type2) do |b, args| + adjust_calc_type(b, type2, type1, args[0]) end end [uint8, uint16, uint32, uint64, int8, int16, int32, int64, float32, float64].each do |type| - no_args_primitive(type, "to_i", int32) do |b, f| - adjust_calc_type(b, int32, type, f.params[0]) + no_args_primitive(type, "to_i", int32) do |b, args| + adjust_calc_type(b, int32, type, args[0]) end - no_args_primitive(type, "to_f", float64) do |b, f| - adjust_calc_type(b, float64, type, f.params[0]) + no_args_primitive(type, "to_f", float64) do |b, args| + adjust_calc_type(b, float64, type, args[0]) end - no_args_primitive(type, "to_f64", float64) do |b, f| - adjust_calc_type(b, float64, type, f.params[0]) + no_args_primitive(type, "to_f64", float64) do |b, args| + adjust_calc_type(b, float64, type, args[0]) end - no_args_primitive(type, "to_f32", float32) do |b, f| - adjust_calc_type(b, float32, type, f.params[0]) + no_args_primitive(type, "to_f32", float32) do |b, args| + adjust_calc_type(b, float32, type, args[0]) end end [uint8, uint16, uint32, uint64, int8, int16, int32, int64].each do |type| - no_args_primitive(type, "chr", char) do |b, f| - adjust_calc_type(b, int8, type, f.params[0]) + no_args_primitive(type, "chr", char) do |b, args| + adjust_calc_type(b, int8, type, args[0]) end end [uint8, uint16, uint32, uint64, int8, int16, int32, int64].repeated_permutation(2) do |type1, type2| [:%, :<<, :>>, :|, :&, :"^"].each do |op| ret_type = greatest_type(type1, type2) - singleton(type1, op, {'other' => type2}, type1) do |b, f| - arg1 = adjust_calc_type(b, ret_type, type1, f.params[0]) - arg2 = adjust_calc_type(b, ret_type, type2, f.params[1]) + singleton(type1, op, {'other' => type2}, type1) do |b, args| + arg1 = adjust_calc_type(b, ret_type, type1, args[0]) + arg2 = adjust_calc_type(b, ret_type, type2, args[1]) ret = build_calc_op(b, ret_type, op, arg1, arg2) cast_back(b, type1, ret_type, ret) end @@ -244,18 +244,18 @@ module Crystal end def define_float32_primitives - singleton(float32, :**, {'other' => float32}, float32) { |b, f, llvm_mod| b.call(powf(llvm_mod), f.params[0], f.params[1]) } + singleton(float32, :**, {'other' => float32}, float32) { |b, args, llvm_mod| b.call(powf(llvm_mod), args[0], args[1]) } end def define_float64_primitives - singleton(float64, :**, {'other' => float64}, float64) { |b, f, llvm_mod| b.call(pow(llvm_mod), f.params[0], f.params[1]) } + singleton(float64, :**, {'other' => float64}, float64) { |b, args, llvm_mod| b.call(pow(llvm_mod), args[0], args[1]) } end def define_symbol_primitives - singleton(symbol, :==, {'other' => symbol}, bool) { |b, f| b.icmp(:eq, f.params[0], f.params[1]) } - singleton(symbol, 'hash', {}, int32) { |b, f| f.params[0] } - no_args_primitive(symbol, 'to_s', string) do |b, f, llvm_mod| - b.load(b.gep llvm_mod.globals['symbol_table'], [LLVM::Int(0), f.params[0]]) + singleton(symbol, :==, {'other' => symbol}, bool) { |b, args| b.icmp(:eq, args[0], args[1]) } + singleton(symbol, 'hash', {}, int32) { |b, args| args[0] } + no_args_primitive(symbol, 'to_s', string) do |b, args, llvm_mod| + b.load(b.gep llvm_mod.globals['symbol_table'], [LLVM::Int(0), args[0]]) end end @@ -269,8 +269,8 @@ module Crystal pointer.add_def Def.new(:+, [Arg.new_with_restriction('offset', Ident.new(["Int64"], true))], PointerAdd.new) pointer.add_def Def.new(:-, [Arg.new_with_restriction('other', SelfType.instance)], PointerDiff.new) pointer.add_def Def.new('as', [Arg.new('type')], PointerCast.new) - shared_singleton(pointer, 'address', uint64) do |b, f, llvm_mod, self_type| - b.ptr2int(f.params[0], LLVM::UInt64) + shared_singleton(pointer, 'address', uint64) do |b, args, llvm_mod, self_type| + b.ptr2int(args[0], LLVM::UInt64) end end @@ -291,7 +291,7 @@ module Crystal end def self_primitive(owner, name) - no_args_primitive(owner, name, owner) { |b, f| f.params[0] } + no_args_primitive(owner, name, owner) { |b, args| args[0] } end def singleton(owner, name, args, return_type, &block) diff --git a/lib/crystal/type_inference.rb b/lib/crystal/type_inference.rb index 93a93556663fb473d2b95e0179842e2d29b6eed7..066d211b2e921c90495b6c4d2df81c431987fdb8 100644 --- a/lib/crystal/type_inference.rb +++ b/lib/crystal/type_inference.rb @@ -443,7 +443,10 @@ module Crystal end def visit_var(node) - var = lookup_var node.name + var = @vars[node.name] + var.used = true + + # var = lookup_var node.name filter = build_var_filter var node.bind_to(filter || var) node.type_filters = and_type_filters({node.name => NotNilFilter}, var.type_filters) diff --git a/lib/crystal/type_inference/after_type_inference_transformer.rb b/lib/crystal/type_inference/after_type_inference_transformer.rb index a55fce2beb8086f7c63c9c41170b87724f8ef1ea..e603c909be0b890b701819e68682eb9c1b0c1b28 100644 --- a/lib/crystal/type_inference/after_type_inference_transformer.rb +++ b/lib/crystal/type_inference/after_type_inference_transformer.rb @@ -28,6 +28,16 @@ module Crystal end class AfterTypeInferenceTransformer < Transformer + @@removed_assigns = 0 + @@removed_no_effect = 0 + @@removed_temps = 0 + + at_exit do + puts "Removed assigns: #{@@removed_assigns}" + puts "Removed no effect: #{@@removed_no_effect}" + puts "Removed temps: #{@@removed_temps}" + end + def initialize(program) @program = program @transformed = {} @@ -49,9 +59,15 @@ module Crystal exps = [] found_no_return = false - node.expressions.each do |exp| + length = node.expressions.length + node.expressions.each_with_index do |exp, i| new_exp = exp.transform(self) if new_exp + if i < length - 1 && (new_exp.is_a?(Var) || new_exp.is_a?(NilLiteral) || new_exp.is_a?(NumberLiteral)) + @@removed_no_effect += 1 + next + end + if new_exp.is_a?(Expressions) exps.concat new_exp.expressions else @@ -67,14 +83,20 @@ module Crystal case exps.length when 0 - nil + return nil when 1 - exps[0] - else - node.expressions = exps - rebind_node node, exps.last - node + return exps[0] + when 2 + first, second = exps + if first.is_a?(Assign) && first.target.is_a?(Var) && second.is_a?(Var) && first.target.name == second.name + @@removed_temps += 1 + return first.value + end end + + node.expressions = exps + rebind_node node, exps.last + node end def transform_assign(node) @@ -85,6 +107,13 @@ module Crystal return node.value end + if node.target.is_a?(Var) && node.target.type + unless node.target.dependencies[0].used + @@removed_assigns += 1 + return node.value + end + end + node end @@ -140,14 +169,14 @@ module Crystal end def check_comparison_of_unsigned_integer_with_zero_or_negative_literal(node) - if (node.name == :< || node.name == :<=) && node.obj.type.integer? && node.obj.type.unsigned? + if (node.name == :< || node.name == :<=) && node.obj && node.obj.type && node.obj.type.integer? && node.obj.type.unsigned? arg = node.args[0] if arg.is_a?(NumberLiteral) && arg.integer? && arg.value.to_i <= 0 node.raise "'#{node.name}' comparison of unsigned integer with zero or negative literal will always be false" end end - if (node.name == :> || node.name == :>=) && node.obj.is_a?(NumberLiteral) && node.obj.integer? && node.obj.value.to_i <= 0 + if (node.name == :> || node.name == :>=) && node.obj && node.obj.type && node.obj.is_a?(NumberLiteral) && node.obj.integer? && node.obj.value.to_i <= 0 arg = node.args[0] if arg.type.integer? && arg.type.unsigned? node.raise "'#{node.name}' comparison of unsigned integer with zero or negative literal will always be false" diff --git a/lib/crystal/type_inference/ast.rb b/lib/crystal/type_inference/ast.rb index f8ec7bbb316bf41aae4f3a15c57469ba5a370548..901127db142851489c2ab4c61074d6aa293183b7 100644 --- a/lib/crystal/type_inference/ast.rb +++ b/lib/crystal/type_inference/ast.rb @@ -6,6 +6,8 @@ module Crystal end class Var + attr_accessor :used + def out? out end