From a8c2a2e8d3fcbca118f873f65605e9fff2a90826 Mon Sep 17 00:00:00 2001 From: Juan Wajnerman <jwajnerman@manas.com.ar> Date: Fri, 23 Aug 2013 14:38:23 -0300 Subject: [PATCH] bootstrap codegen: basic implementation of local vars --- bootstrap/crystal/codegen.cr | 129 +++++++++++++++++++++ bootstrap/crystal/llvm.cr | 15 +++ bootstrap/spec/crystal/codegen/var_spec.cr | 7 ++ 3 files changed, 151 insertions(+) create mode 100644 bootstrap/spec/crystal/codegen/var_spec.cr diff --git a/bootstrap/crystal/codegen.cr b/bootstrap/crystal/codegen.cr index 02fcdbaf5d..0b462ecb75 100644 --- a/bootstrap/crystal/codegen.cr +++ b/bootstrap/crystal/codegen.cr @@ -29,6 +29,16 @@ module Crystal visitor.llvm_mod end + class LLVMVar + property :pointer + property :type + + def initialize(pointer, type) + @pointer = pointer + @type = type + end + end + class CodeGenVisitor < Visitor getter :llvm_mod getter :fun @@ -47,6 +57,7 @@ module Crystal @builder = LLVM::Builder.new @alloca_block, @const_block, @entry_block = new_entry_block_chain ["alloca", "const", "entry"] @const_block_entry = @const_block + @vars = {} of String => LLVMVar end def finish @@ -95,6 +106,102 @@ module Crystal @last = LLVM::Int8.from_i(node.value[0].ord) end + def visit(node : Assign) + codegen_assign_node(node.target, node.value) + end + + def visit(node : Var) + var = @vars[node.name] + @last = @builder.load(var.pointer) + # if var[:type] == node.type + # @last = var[:ptr] + # @last = @builder.load(@last, node.name) unless (var[:treated_as_pointer] || var[:type].union?) + # elsif var[:type].nilable? + # if node.type.nil_type? + # @last = null_pointer?(var[:ptr]) + # else + # @last = var[:ptr] + # @last = @builder.load(@last, node.name) unless (var[:treated_as_pointer] || var[:type].union?) + # end + # else + # if node.type.union? + # @last = cast_to_pointer var[:ptr], node.type + # else + # value_ptr = union_value(var[:ptr]) + # @last = cast_to_pointer value_ptr, node.type + # @last = @builder.load(@last) unless node.type.passed_by_val? + # end + # end + end + + def codegen_assign_node(target, value) + if target.is_a?(Ident) + return false + end + + # if target.is_a?(ClassVar) && target.class_scope + # global_name = class_var_global_name(target) + # in_const_block(global_name) do + # accept(value) + # llvm_value = @last + # ptr = assign_to_global global_name, target.type + # codegen_assign(ptr, target.type, value.type, llvm_value) + # end + # return + # end + + accept(value) + + # if value.no_returns? + # return + # end + + codegen_assign_target(target, value, @last) if @last + + false + end + + def codegen_assign_target(target, value, llvm_value) + case target + # when InstanceVar + # ivar = @type.lookup_instance_var(target.name.to_s) + # ptr = gep llvm_self_ptr, 0, @type.index_of_instance_var(target.name.to_s) + # when Global + # ptr = assign_to_global target.name.to_s, target.type + # when ClassVar + # ptr = assign_to_global class_var_global_name(target), target.type + # else + when Var + var = declare_var(target) + ptr = var.pointer + codegen_assign(ptr, target.type, value.type, llvm_value) + else + raise "Unknown assign target type: #{target}" + end + + # codegen_assign(ptr, target.type, value.type, llvm_value) + end + + def codegen_assign(pointer, target_type, value_type, value, instance_var = false) + if target_type == value_type + # value = @builder.load value if target_type.union? || (instance_var && (target_type.c_struct? || target_type.c_union?)) + @builder.store value, pointer + else + raise "Not implemented: assign_to_union" + # assign_to_union(pointer, target_type, value_type, value) + end + end + + def declare_var(var) + @vars.fetch_or_assign(var.name.to_s) do + llvm_var = LLVMVar.new(alloca(llvm_type(var.type), var.name.to_s), var.type) + # if var.type.is_a?(UnionType) && union_type_id = var.type.types.any?(&:nil_type?) + # in_alloca_block { assign_to_union(llvm_var[:ptr], var.type, @mod.nil, llvm_nil) } + # end + llvm_var + end + end + # def new_entry_block # @alloca_block, @entry_block = new_entry_block_chain "alloca", "entry" # end @@ -127,5 +234,27 @@ module Crystal def new_blocks(names) names.map { |name| new_block name } end + + def alloca(type, name = "") + in_alloca_block { @builder.alloca type, name } + end + + def in_alloca_block + old_block = @builder.insert_block + @builder.position_at_end @alloca_block + value = yield + @builder.position_at_end old_block + value + end + + def llvm_type(type) + @llvm_typer.llvm_type(type) + end + + def accept(node) + # old_current_node = @current_node + node.accept self + # @current_node = old_current_node + end end end diff --git a/bootstrap/crystal/llvm.cr b/bootstrap/crystal/llvm.cr index 0db0953699..1a468f27c9 100644 --- a/bootstrap/crystal/llvm.cr +++ b/bootstrap/crystal/llvm.cr @@ -21,6 +21,9 @@ lib LibLLVM("LLVM-3.3") fun build_ret = LLVMBuildRet(builder : BuilderRef, value : ValueRef) : ValueRef fun build_br = LLVMBuildBr(builder : BuilderRef, block : BasicBlockRef) : ValueRef fun build_call = LLVMBuildCall(builder : BuilderRef, fn : ValueRef, args : ValueRef*, num_args : Int32, name : Char*) : ValueRef + fun build_alloca = LLVMBuildAlloca(builder : BuilderRef, type : TypeRef, name : Char*) : ValueRef + fun build_store = LLVMBuildStore(builder : BuilderRef, value : ValueRef, ptr : ValueRef) + fun build_load = LLVMBuildLoad(builder : BuilderRef, ptr : ValueRef, name : Char*) : ValueRef fun int_type = LLVMIntType(bits : Int32) : TypeRef fun float_type = LLVMFloatType() : TypeRef fun double_type = LLVMDoubleType() : TypeRef @@ -126,6 +129,18 @@ module LLVM def call(func) LibLLVM.build_call(@builder, func.llvm_function, nil, 0, "") end + + def alloca(type, name = "") + LibLLVM.build_alloca(@builder, type.type, name) + end + + def store(value, ptr) + LibLLVM.build_store(@builder, value, ptr) + end + + def load(ptr, name = "") + LibLLVM.build_load(@builder, ptr, name) + end end abstract class Type diff --git a/bootstrap/spec/crystal/codegen/var_spec.cr b/bootstrap/spec/crystal/codegen/var_spec.cr new file mode 100644 index 0000000000..a99feb89f8 --- /dev/null +++ b/bootstrap/spec/crystal/codegen/var_spec.cr @@ -0,0 +1,7 @@ +require "../../spec_helper" + +describe "Code gen: var" do + it "codegens var" do + run("a = 1; 1.5; a").to_i.should eq(1) + end +end -- GitLab