diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 4b4c5cc04ce2..78f64473020e 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -70,6 +70,9 @@ jobs: - llvm_version: 14.0.0 llvm_url: https://github.com/llvm/llvm-project/releases/download/llvmorg-14.0.0/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz base_image: ubuntu-22.04 + - llvm_version: 15.0.6 + llvm_url: https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.6/clang+llvm-15.0.6-x86_64-linux-gnu-ubuntu-18.04.tar.xz + base_image: ubuntu-22.04 runs-on: ${{ matrix.base_image }} name: "Test LLVM ${{ matrix.llvm_version }} (${{ matrix.base_image }})" steps: diff --git a/samples/llvm/brainfuck.cr b/samples/llvm/brainfuck.cr index b6cba540f18f..5c005e72003c 100644 --- a/samples/llvm/brainfuck.cr +++ b/samples/llvm/brainfuck.cr @@ -17,11 +17,11 @@ class Increment < Instruction builder = program.builder builder.position_at_end bb - cell_index = builder.load program.cell_index_ptr, "cell_index" - current_cell_ptr = builder.gep program.cells_ptr, cell_index, "current_cell_ptr" + cell_index = builder.load program.ctx.int32, program.cell_index_ptr, "cell_index" + current_cell_ptr = builder.gep program.cell_type, program.cells_ptr, cell_index, "current_cell_ptr" - cell_val = builder.load current_cell_ptr, "cell_value" - increment_amount = program.ctx.int(CELL_SIZE_IN_BYTES * 8).const_int(@amount) + cell_val = builder.load program.cell_type, current_cell_ptr, "cell_value" + increment_amount = program.cell_type.const_int(@amount) new_cell_val = builder.add cell_val, increment_amount, "cell_value" builder.store new_cell_val, current_cell_ptr @@ -37,7 +37,7 @@ class DataIncrement < Instruction builder = program.builder builder.position_at_end bb - cell_index = builder.load program.cell_index_ptr, "cell_index" + cell_index = builder.load program.ctx.int32, program.cell_index_ptr, "cell_index" increment_amount = program.ctx.int32.const_int(@amount) new_cell_index = builder.add cell_index, increment_amount, "new_cell_index" @@ -52,11 +52,10 @@ class Read < Instruction builder = program.builder builder.position_at_end bb - cell_index = builder.load program.cell_index_ptr, "cell_index" - current_cell_ptr = builder.gep program.cells_ptr, cell_index, "current_cell_ptr" + cell_index = builder.load program.ctx.int32, program.cell_index_ptr, "cell_index" + current_cell_ptr = builder.gep program.cell_type, program.cells_ptr, cell_index, "current_cell_ptr" - getchar = program.mod.functions["getchar"] - input_char = builder.call getchar, "input_char" + input_char = program.call_c_function "getchar", name: "input_char" input_byte = builder.trunc input_char, program.ctx.int8, "input_byte" builder.store input_byte, current_cell_ptr @@ -69,14 +68,13 @@ class Write < Instruction builder = program.builder builder.position_at_end bb - cell_index = builder.load program.cell_index_ptr, "cell_index" - current_cell_ptr = builder.gep program.cells_ptr, cell_index, "current_cell_ptr" + cell_index = builder.load program.ctx.int32, program.cell_index_ptr, "cell_index" + current_cell_ptr = builder.gep program.cell_type, program.cells_ptr, cell_index, "current_cell_ptr" - cell_val = builder.load current_cell_ptr, "cell_value" + cell_val = builder.load program.cell_type, current_cell_ptr, "cell_value" cell_val_as_char = builder.sext cell_val, program.ctx.int32, "cell_val_as_char" - putchar = program.mod.functions["putchar"] - builder.call putchar, cell_val_as_char + program.call_c_function "putchar", cell_val_as_char bb end @@ -99,10 +97,10 @@ class Loop < Instruction loop_after = func.basic_blocks.append "loop_after" builder.position_at_end loop_header - cell_index = builder.load program.cell_index_ptr, "cell_index" - current_cell_ptr = builder.gep program.cells_ptr, cell_index, "current_cell_ptr" - cell_val = builder.load current_cell_ptr, "cell_value" - zero = program.ctx.int(CELL_SIZE_IN_BYTES * 8).const_int(0) + cell_index = builder.load program.ctx.int32, program.cell_index_ptr, "cell_index" + current_cell_ptr = builder.gep program.cell_type, program.cells_ptr, cell_index, "current_cell_ptr" + cell_val = builder.load program.cell_type, current_cell_ptr, "cell_value" + zero = program.cell_type.const_int(0) cell_val_is_zero = builder.icmp LLVM::IntPredicate::EQ, cell_val, zero builder.cond cell_val_is_zero, loop_after, loop_body_block @@ -123,14 +121,19 @@ class Program getter ctx : LLVM::Context getter builder : LLVM::Builder getter instructions + getter cell_type : LLVM::Type getter! cells_ptr : LLVM::Value getter! cell_index_ptr : LLVM::Value getter! func : LLVM::Function + @func_types = {} of String => LLVM::Type + def initialize(@instructions : Array(Instruction)) @ctx = LLVM::Context.new @mod = @ctx.new_module("brainfuck") @builder = @ctx.new_builder + + @cell_type = @ctx.int(CELL_SIZE_IN_BYTES * 8) end def self.new(source : String) @@ -205,10 +208,21 @@ class Program end def declare_c_functions(mod) - mod.functions.add "calloc", [@ctx.int32, @ctx.int32], @ctx.void_pointer - mod.functions.add "free", [@ctx.void_pointer], @ctx.void - mod.functions.add "putchar", [@ctx.int32], @ctx.int32 - mod.functions.add "getchar", ([] of LLVM::Type), @ctx.int32 + declare_c_function mod, "calloc", [@ctx.int32, @ctx.int32], @ctx.void_pointer + declare_c_function mod, "free", [@ctx.void_pointer], @ctx.void + declare_c_function mod, "putchar", [@ctx.int32], @ctx.int32 + declare_c_function mod, "getchar", ([] of LLVM::Type), @ctx.int32 + end + + def declare_c_function(mod, name, param_types, return_type) + func_type = LLVM::Type.function(param_types, return_type) + @func_types[name] = func_type + mod.functions.add name, func_type + end + + def call_c_function(func_name, args = [] of LLVM::Value, name = "") + func = mod.functions[func_name] + @builder.call @func_types[func_name], func, args, name end def create_main(mod) @@ -222,7 +236,7 @@ class Program calloc = mod.functions["calloc"] call_args = [@ctx.int32.const_int(NUM_CELLS), @ctx.int32.const_int(CELL_SIZE_IN_BYTES)] - @cells_ptr = builder.call calloc, call_args, "cells" + @cells_ptr = call_c_function "calloc", call_args, "cells" @cell_index_ptr = builder.alloca @ctx.int32, "cell_index_ptr" zero = @ctx.int32.const_int(0) @@ -232,8 +246,7 @@ class Program def add_cells_cleanup(mod, bb) builder.position_at_end bb - free = mod.functions["free"] - builder.call free, cells_ptr + call_c_function "free", cells_ptr zero = @ctx.int32.const_int(0) builder.ret zero diff --git a/spec/compiler/codegen/pointer_spec.cr b/spec/compiler/codegen/pointer_spec.cr index 8e5cd488fd52..60b7718771c8 100644 --- a/spec/compiler/codegen/pointer_spec.cr +++ b/spec/compiler/codegen/pointer_spec.cr @@ -373,8 +373,6 @@ describe "Code gen: pointer" do it "can assign nil to void pointer" do codegen(%( - require "prelude" - ptr = Pointer(Void).malloc(1_u64) ptr.value = ptr.value )) diff --git a/spec/compiler/codegen/primitives_spec.cr b/spec/compiler/codegen/primitives_spec.cr index cbe5695beb4f..2434beabbf8f 100644 --- a/spec/compiler/codegen/primitives_spec.cr +++ b/spec/compiler/codegen/primitives_spec.cr @@ -277,8 +277,9 @@ describe "Code gen: primitives" do list = VaList.new list.next(Int32) )) + type = {% if LibLLVM::IS_LT_150 %} "%VaList*" {% else %} "ptr" {% end %} str = mod.to_s - str.should contain("va_arg %VaList* %list") + str.should contain("va_arg #{type} %list") end it "works with C code" do diff --git a/spec/llvm-ir/test.sh b/spec/llvm-ir/test.sh index 6c783ae5b4f7..48a8f38d4d8b 100755 --- a/spec/llvm-ir/test.sh +++ b/spec/llvm-ir/test.sh @@ -1,5 +1,8 @@ #!/bin/bash +# TODO: the specs in this folder still expect typed pointers and so will fail +# on LLVM 15+ which use opaque pointers + set -euo pipefail SCRIPT_PATH="$(realpath "$0")" diff --git a/src/compiler/crystal/codegen/asm.cr b/src/compiler/crystal/codegen/asm.cr index 0d4945106fc6..f0b3e2f088ab 100644 --- a/src/compiler/crystal/codegen/asm.cr +++ b/src/compiler/crystal/codegen/asm.cr @@ -55,7 +55,7 @@ class Crystal::CodeGenVisitor value = fun_type.inline_asm(node.text, constraints, node.volatile?, node.alignstack?, node.can_throw?) value = LLVM::Function.from_value(value) - asm_value = call value, input_values + asm_value = call LLVMTypedFunction.new(fun_type, value), input_values if ptrofs = node.output_ptrofs if ptrofs.size > 1 diff --git a/src/compiler/crystal/codegen/ast.cr b/src/compiler/crystal/codegen/ast.cr index bae46133d4bd..816958844dd9 100644 --- a/src/compiler/crystal/codegen/ast.cr +++ b/src/compiler/crystal/codegen/ast.cr @@ -85,6 +85,10 @@ module Crystal @c_calling_convention ? self : nil end + def llvm_intrinsic? + self.is_a?(External) && self.real_name.starts_with?("llvm.") + end + private def compute_c_calling_convention # One case where this is not true if for LLVM intrinsics. # For example overflow intrinsics return a tuple, like {i32, i1}: diff --git a/src/compiler/crystal/codegen/call.cr b/src/compiler/crystal/codegen/call.cr index 2f92d217ff80..d0937cfff40c 100644 --- a/src/compiler/crystal/codegen/call.cr +++ b/src/compiler/crystal/codegen/call.cr @@ -95,7 +95,7 @@ class Crystal::CodeGenVisitor # - C calling convention passing needs a separate handling of pass-by-value # - Primitives might need a separate handling (for example invoking a Proc) if arg.type.passed_by_value? && !c_calling_convention && !is_primitive - call_arg = load(call_arg) + call_arg = load(llvm_type(arg.type), call_arg) end call_args << call_arg @@ -205,7 +205,7 @@ class Crystal::CodeGenVisitor abi_arg_type = abi_info.arg_types[i] case abi_arg_type.kind in .direct? - call_arg = codegen_direct_abi_call(call_arg, abi_arg_type) unless arg.type.nil_type? + call_arg = codegen_direct_abi_call(arg.type, call_arg, abi_arg_type) unless arg.type.nil_type? in .indirect? call_arg = codegen_indirect_abi_call(call_arg, abi_arg_type) unless arg.type.nil_type? in .ignore? @@ -233,16 +233,16 @@ class Crystal::CodeGenVisitor call_args end - def codegen_direct_abi_call(call_arg, abi_arg_type) + def codegen_direct_abi_call(call_arg_type, call_arg, abi_arg_type) if cast = abi_arg_type.cast final_value = alloca cast - final_value_casted = bit_cast final_value, llvm_context.void_pointer - gep_call_arg = bit_cast gep(call_arg, 0, 0), llvm_context.void_pointer + final_value_casted = cast_to_void_pointer final_value + gep_call_arg = cast_to_void_pointer gep(llvm_type(call_arg_type), call_arg, 0, 0) size = @abi.size(abi_arg_type.type) size = @program.bits64? ? int64(size) : int32(size) align = @abi.align(abi_arg_type.type) memcpy(final_value_casted, gep_call_arg, size, align, int1(0)) - call_arg = load final_value + call_arg = load cast, final_value else # Keep same call arg end @@ -253,8 +253,8 @@ class Crystal::CodeGenVisitor # and *replace* the call argument by a pointer to the allocated memory def codegen_indirect_abi_call(call_arg, abi_arg_type) final_value = alloca abi_arg_type.type - final_value_casted = bit_cast final_value, llvm_context.void_pointer - call_arg_casted = bit_cast call_arg, llvm_context.void_pointer + final_value_casted = cast_to_void_pointer final_value + call_arg_casted = cast_to_void_pointer call_arg size = @abi.size(abi_arg_type.type) size = @program.bits64? ? int64(size) : int32(size) align = @abi.align(abi_arg_type.type) @@ -513,7 +513,7 @@ class Crystal::CodeGenVisitor external = target_def.try &.c_calling_convention? if external && (external.type.proc? || external.type.is_a?(NilableProcType)) - fun_ptr = bit_cast(@last, llvm_context.void_pointer) + fun_ptr = cast_to_void_pointer(@last) ctx_ptr = llvm_context.void_pointer.null return @last = make_fun(external.type, fun_ptr, ctx_ptr) end @@ -528,9 +528,9 @@ class Crystal::CodeGenVisitor if cast = abi_return.cast cast1 = alloca cast store @last, cast1 - cast2 = bit_cast cast1, llvm_context.void_pointer + cast2 = cast_to_void_pointer(cast1) final_value = alloca abi_return.type - final_value_casted = bit_cast final_value, llvm_context.void_pointer + final_value_casted = cast_to_void_pointer final_value size = @abi.size(abi_return.type) size = @program.@program.bits64? ? int64(size) : int32(size) align = @abi.align(abi_return.type) diff --git a/src/compiler/crystal/codegen/cast.cr b/src/compiler/crystal/codegen/cast.cr index e069e8f6559d..9f8111a5a4dc 100644 --- a/src/compiler/crystal/codegen/cast.cr +++ b/src/compiler/crystal/codegen/cast.cr @@ -126,6 +126,7 @@ class Crystal::CodeGenVisitor types_needing_cast.each_with_index do |type_needing_cast, i| # Find compatible type compatible_type = target_type.union_types.find! { |ut| type_needing_cast.implements?(ut) } + llvm_compatible_type = llvm_type(compatible_type) matches_label, doesnt_match_label = new_blocks "matches", "doesnt_match_label" cmp_result = equal?(value_type_id, type_id(type_needing_cast)) @@ -135,9 +136,9 @@ class Crystal::CodeGenVisitor # Store value casted_value = cast_to_pointer(union_value_ptr, type_needing_cast) - compatible_ptr = alloca llvm_type(compatible_type) + compatible_ptr = alloca llvm_compatible_type assign(compatible_ptr, compatible_type, type_needing_cast, casted_value) - store_in_union target_type, target_pointer, compatible_type, load(compatible_ptr) + store_in_union target_type, target_pointer, compatible_type, load(llvm_compatible_type, compatible_ptr) br exit_label position_at_end doesnt_match_label @@ -167,7 +168,7 @@ class Crystal::CodeGenVisitor end def assign_distinct(target_pointer, target_type : MixedUnionType, value_type : VoidType, value) - store_void_in_union target_pointer, target_type + store_void_in_union target_type, target_pointer end def assign_distinct(target_pointer, target_type : MixedUnionType, value_type : BoolType, value) @@ -175,7 +176,7 @@ class Crystal::CodeGenVisitor end def assign_distinct(target_pointer, target_type : MixedUnionType, value_type : NilType, value) - store_nil_in_union target_pointer, target_type + store_nil_in_union target_type, target_pointer end def assign_distinct(target_pointer, target_type : MixedUnionType, value_type : Type, value) @@ -197,7 +198,7 @@ class Crystal::CodeGenVisitor def assign_distinct(target_pointer, target_type : VirtualType, value_type : MixedUnionType, value) _, union_value_ptr = union_type_and_value_pointer(value, value_type) casted_value = cast_to_pointer(union_value_ptr, target_type) - store load(casted_value), target_pointer + store load(llvm_type(target_type), casted_value), target_pointer end def assign_distinct(target_pointer, target_type : VirtualType, value_type : Type, value) @@ -212,7 +213,7 @@ class Crystal::CodeGenVisitor # Can happen when assigning Foo+.class <- Bar.class | Baz.class with Bar < Foo and Baz < Foo _, union_value_ptr = union_type_and_value_pointer(value, value_type) casted_value = cast_to_pointer(union_value_ptr, target_type) - store load(casted_value), target_pointer + store load(llvm_type(target_type), casted_value), target_pointer end def assign_distinct(target_pointer, target_type : NilableProcType, value_type : NilType, value) @@ -229,10 +230,13 @@ class Crystal::CodeGenVisitor end def assign_distinct(target_pointer, target_type : TupleInstanceType, value_type : TupleInstanceType, value) + target_struct_type = llvm_type(target_type) + value_struct_type = llvm_type(value_type) + index = 0 target_type.tuple_types.zip(value_type.tuple_types) do |target_tuple_type, value_tuple_type| - target_ptr = gep target_pointer, 0, index - value_ptr = gep value, 0, index + target_ptr = aggregate_index(target_struct_type, target_pointer, index) + value_ptr = aggregate_index(value_struct_type, value, index) loaded_value = to_lhs(value_ptr, value_tuple_type) assign(target_ptr, target_tuple_type, value_tuple_type, loaded_value) index += 1 @@ -241,12 +245,16 @@ class Crystal::CodeGenVisitor end def assign_distinct(target_pointer, target_type : NamedTupleInstanceType, value_type : NamedTupleInstanceType, value) + target_struct_type = llvm_type(target_type) + value_struct_type = llvm_type(value_type) + value_type.entries.each_with_index do |entry, index| - value_ptr = aggregate_index(value, index) + value_ptr = aggregate_index(value_struct_type, value, index) value_at_index = to_lhs(value_ptr, entry.type) target_index = target_type.name_index(entry.name).not_nil! target_index_type = target_type.name_type(entry.name) - assign aggregate_index(target_pointer, target_index), target_index_type, entry.type, value_at_index + target_ptr = aggregate_index(target_struct_type, target_pointer, target_index) + assign target_ptr, target_index_type, entry.type, value_at_index end end @@ -262,26 +270,28 @@ class Crystal::CodeGenVisitor # In that case we can simply get the union value and cast it to the target type. # Cast of a non-void proc to a void proc _, union_value_ptr = union_type_and_value_pointer(value, value_type) - value = bit_cast(union_value_ptr, llvm_type(target_type).pointer) - store load(value), target_pointer + value = cast_to_pointer(union_value_ptr, target_type) + store load(llvm_type(target_type), value), target_pointer end def assign_distinct(target_pointer, target_type : Type, value_type : Type, value) raise "BUG: trying to assign #{target_type} (#{target_type.class}) <- #{value_type} (#{value_type.class})" end - def downcast(value, to_type, from_type : VoidType, already_loaded) + def downcast(value, to_type, from_type : VoidType, already_loaded, *, extern = false) value end - def downcast(value, to_type, from_type : Type, already_loaded) + # *extern* is only used in `CodeGenVisitor#read_instance_var`, and it is + # irrelevant whenever `already_loaded == true` + def downcast(value, to_type, from_type : Type, already_loaded, *, extern = false) return llvm_nil if @builder.end from_type = from_type.remove_indirection to_type = to_type.remove_indirection unless already_loaded - value = to_lhs(value, from_type) + value = extern ? extern_to_lhs(value, from_type) : to_lhs(value, from_type) end if from_type != to_type value = downcast_distinct value, to_type, from_type @@ -329,7 +339,7 @@ class Crystal::CodeGenVisitor def downcast_distinct(value, to_type : PointerInstanceType, from_type : PointerInstanceType) # cast of a pointer being cast to Void* - bit_cast value, llvm_context.void_pointer + cast_to_void_pointer value end def downcast_distinct(value, to_type : ReferenceUnionType, from_type : ReferenceUnionType) @@ -406,13 +416,13 @@ class Crystal::CodeGenVisitor def downcast_distinct(value, to_type : NilableType, from_type : MixedUnionType) _, value_ptr = union_type_and_value_pointer(value, from_type) - load cast_to_pointer(value_ptr, to_type) + load(llvm_type(to_type), cast_to_pointer(value_ptr, to_type)) end def downcast_distinct(value, to_type : BoolType, from_type : MixedUnionType) _, value_ptr = union_type_and_value_pointer(value, from_type) value = cast_to_pointer(value_ptr, @program.int8) - value = load(value) + value = load(llvm_context.int8, value) trunc value, llvm_context.int1 end @@ -440,11 +450,14 @@ class Crystal::CodeGenVisitor end def downcast_distinct(value, to_type : TupleInstanceType, from_type : TupleInstanceType) - target_pointer = alloca(llvm_type(to_type)) + target_struct_type = llvm_type(to_type) + value_struct_type = llvm_type(from_type) + target_pointer = alloca(target_struct_type) + index = 0 to_type.tuple_types.zip(from_type.tuple_types) do |target_tuple_type, value_tuple_type| - target_ptr = gep target_pointer, 0, index - value_ptr = gep value, 0, index + target_ptr = aggregate_index(target_struct_type, target_pointer, index) + value_ptr = aggregate_index(value_struct_type, value, index) loaded_value = to_lhs(value_ptr, value_tuple_type) downcasted_value = downcast(loaded_value, target_tuple_type, value_tuple_type, true) downcasted_value = to_rhs(downcasted_value, target_tuple_type) @@ -455,15 +468,18 @@ class Crystal::CodeGenVisitor end def downcast_distinct(value, to_type : NamedTupleInstanceType, from_type : NamedTupleInstanceType) - target_pointer = alloca(llvm_type(to_type)) + target_struct_type = llvm_type(to_type) + value_struct_type = llvm_type(from_type) + target_pointer = alloca(target_struct_type) + from_type.entries.each_with_index do |entry, index| - value_ptr = aggregate_index(value, index) + value_ptr = aggregate_index(value_struct_type, value, index) value_at_index = to_lhs(value_ptr, entry.type) target_index = to_type.name_index(entry.name).not_nil! target_index_type = to_type.name_type(entry.name) downcasted_value = downcast(value_at_index, target_index_type, entry.type, true) downcasted_value = to_rhs(downcasted_value, target_index_type) - store downcasted_value, aggregate_index(target_pointer, target_index) + store downcasted_value, aggregate_index(target_struct_type, target_pointer, target_index) end target_pointer end @@ -603,7 +619,7 @@ class Crystal::CodeGenVisitor def upcast_distinct(value, to_type : MixedUnionType, from_type : VoidType) union_ptr = alloca(llvm_type(to_type)) - store_void_in_union union_ptr, to_type + store_void_in_union to_type, union_ptr union_ptr end @@ -615,7 +631,7 @@ class Crystal::CodeGenVisitor def upcast_distinct(value, to_type : MixedUnionType, from_type : NilType) union_ptr = alloca(llvm_type(to_type)) - store_nil_in_union union_ptr, to_type + store_nil_in_union to_type, union_ptr union_ptr end diff --git a/src/compiler/crystal/codegen/class_var.cr b/src/compiler/crystal/codegen/class_var.cr index 1e3df6bc1358..07d8ed0f96b1 100644 --- a/src/compiler/crystal/codegen/class_var.cr +++ b/src/compiler/crystal/codegen/class_var.cr @@ -82,7 +82,7 @@ class Crystal::CodeGenVisitor def initialize_class_var(class_var : MetaTypeVar, initializer : ClassVarInitializer) init_func = create_initialize_class_var_function(class_var, initializer) - init_func = check_main_fun(init_func.name, init_func) if init_func + init_func = check_main_fun(init_func.func.name, init_func) if init_func # For unsafe class var we just initialize them without # using a flag to know if they were initialized @@ -116,7 +116,7 @@ class Crystal::CodeGenVisitor node = initializer.node init_function_name = "~#{class_var_global_initialized_name(class_var)}" - @main_mod.functions[init_function_name]? || begin + typed_fun?(@main_mod, init_function_name) || begin global = declare_class_var(class_var) discard = false @@ -155,7 +155,7 @@ class Crystal::CodeGenVisitor if discard class_var.simple_initializer = true - new_func.delete + new_func.func.delete nil else new_func @@ -199,7 +199,7 @@ class Crystal::CodeGenVisitor func = create_read_class_var_function(class_var, initializer) if func - func = check_main_fun func.name, func + func = check_main_fun func.func.name, func call func else get_class_var_global(class_var) @@ -216,7 +216,7 @@ class Crystal::CodeGenVisitor def read_virtual_class_var_ptr(class_var, owner) self_type_id = type_id(llvm_self, owner) read_function_name = "~#{class_var_global_name(class_var)}:read" - func = @main_mod.functions[read_function_name]? || + func = typed_fun?(@main_mod, read_function_name) || create_read_virtual_class_var_ptr_function(read_function_name, class_var, owner) func = check_main_fun read_function_name, func call func, self_type_id @@ -263,7 +263,7 @@ class Crystal::CodeGenVisitor def read_virtual_metaclass_class_var_ptr(class_var, owner) self_type_id = type_id(llvm_self, owner) read_function_name = "~#{class_var_global_name(class_var)}:read" - func = @main_mod.functions[read_function_name]? || + func = typed_fun?(@main_mod, read_function_name) || create_read_virtual_metaclass_var_ptr_function(read_function_name, class_var, owner) func = check_main_fun read_function_name, func call func, self_type_id @@ -308,7 +308,7 @@ class Crystal::CodeGenVisitor def create_read_class_var_function(class_var, initializer) fun_name = "~#{class_var_global_name(class_var)}:read" - if func = @main_mod.functions[fun_name]? + if func = typed_fun?(@main_mod, fun_name) return func end @@ -320,7 +320,7 @@ class Crystal::CodeGenVisitor in_main do define_main_function(fun_name, ([] of LLVM::Type), llvm_type(class_var.type).pointer) do |func| set_internal_fun_debug_location(func, fun_name, initializer.node.location) - init_func = check_main_fun init_func.name, init_func + init_func = check_main_fun init_func.func.name, init_func ret lazy_initialize_class_var(initializer.node, init_func, global, initialized_flag) end end diff --git a/src/compiler/crystal/codegen/codegen.cr b/src/compiler/crystal/codegen/codegen.cr index 3ce9fc0bc7c2..562e52eaf1ff 100644 --- a/src/compiler/crystal/codegen/codegen.cr +++ b/src/compiler/crystal/codegen/codegen.cr @@ -27,22 +27,27 @@ module Crystal end def evaluate(node, debug = Debug::Default) - llvm_mod = codegen(node, single_module: true, debug: debug)[""].mod - llvm_mod.target = target_machine.triple + visitor = CodeGenVisitor.new self, node, single_module: true, debug: debug + visitor.accept node + visitor.process_finished_hooks + visitor.finish - main = llvm_mod.functions[MAIN_NAME] + llvm_mod = visitor.modules[""].mod + llvm_mod.target = target_machine.triple - main_return_type = main.return_type + main = visitor.typed_fun?(llvm_mod, MAIN_NAME).not_nil! # It seems the JIT doesn't like it if we return an empty type (struct {}) llvm_context = llvm_mod.context + main_return_type = main.type.return_type main_return_type = llvm_context.void if node.type.nil_type? - wrapper = llvm_mod.functions.add("__evaluate_wrapper", [] of LLVM::Type, main_return_type) do |func| + wrapper_type = LLVM::Type.function([] of LLVM::Type, main_return_type) + wrapper = llvm_mod.functions.add("__evaluate_wrapper", wrapper_type) do |func| func.basic_blocks.append "entry" do |builder| argc = llvm_context.int32.const_int(0) argv = llvm_context.void_pointer.pointer.null - ret = builder.call(main, [argc, argv]) + ret = builder.call(main.type, main.func, [argc, argv]) (node.type.void? || node.type.nil_type?) ? builder.ret : builder.ret(ret) end end @@ -155,20 +160,22 @@ module Crystal @argv : LLVM::Value @rescue_block : LLVM::BasicBlock? @catch_pad : LLVM::Value? - @malloc_fun : LLVM::Function? - @malloc_atomic_fun : LLVM::Function? - @c_malloc_fun : LLVM::Function? + @fun_types : Hash({LLVM::Module, String}, LLVM::Type) @sret_value : LLVM::Value? @cant_pass_closure_to_c_exception_call : Call? - @realloc_fun : LLVM::Function? - @c_realloc_fun : LLVM::Function? - @raise_overflow_fun : LLVM::Function? @main_llvm_context : LLVM::Context @main_llvm_typer : LLVMTyper @main_module_info : ModuleInfo @main_builder : CrystalLLVMBuilder @call_location : Location? + @malloc_fun : LLVMTypedFunction? + @malloc_atomic_fun : LLVMTypedFunction? + @realloc_fun : LLVMTypedFunction? + @raise_overflow_fun : LLVMTypedFunction? + @c_malloc_fun : LLVMTypedFunction? + @c_realloc_fun : LLVMTypedFunction? + def initialize(@program : Program, @node : ASTNode, single_module = false, @debug = Debug::Default) @single_module = !!single_module @abi = @program.target_machine.abi @@ -181,16 +188,18 @@ module Crystal @main_llvm_typer = @llvm_typer @main_ret_type = node.type? || @program.nil_type ret_type = @llvm_typer.llvm_return_type(@main_ret_type) - @main = @llvm_mod.functions.add(MAIN_NAME, [llvm_context.int32, llvm_context.void_pointer.pointer], ret_type) + main_type = LLVM::Type.function([llvm_context.int32, llvm_context.void_pointer.pointer], ret_type) + @main = @llvm_mod.functions.add(MAIN_NAME, main_type) + @fun_types = { {@llvm_mod, MAIN_NAME} => main_type } if @program.has_flag? "windows" @personality_name = "__CxxFrameHandler3" - @main.personality_function = windows_personality_fun + @main.personality_function = windows_personality_fun.func else @personality_name = "__crystal_personality" end - @context = Context.new @main, @program + @context = Context.new @main, main_type, @program @context.return_type = @main_ret_type @argc = @main.params[0] @@ -278,7 +287,7 @@ module Crystal end def wrap_builder(builder) - CrystalLLVMBuilder.new builder, llvm_typer, @program.printf(@llvm_mod, llvm_context) + CrystalLLVMBuilder.new builder, llvm_typer, c_printf_fun end def define_symbol_table(llvm_mod, llvm_typer) @@ -425,7 +434,7 @@ module Crystal end def visit(node : FileNode) - with_context(Context.new(context.fun, context.type)) do + with_context(Context.new(context.fun, context.fun_type, context.type)) do file_module = @program.file_module(node.filename) if vars = file_module.vars? set_current_debug_location Location.new(node.filename, 1, 1) if @debug.line_numbers? @@ -533,13 +542,15 @@ module Crystal def visit(node : NamedTupleLiteral) request_value do type = node.type.as(NamedTupleInstanceType) - struct_type = alloca llvm_type(type) + struct_type = llvm_type(type) + tuple = alloca struct_type node.entries.each do |entry| accept entry.value index = type.name_index(entry.key).not_nil! - assign aggregate_index(struct_type, index), type.entries[index].type, entry.value.type, @last + entry_type = type.entries[index].type + assign aggregate_index(struct_type, tuple, index), entry_type, entry.value.type, @last end - @last = struct_type + @last = tuple end false end @@ -595,9 +606,9 @@ module Crystal the_fun = check_main_fun fun_literal_name, the_fun set_current_debug_location(node) if @debug.line_numbers? - fun_ptr = bit_cast(the_fun, llvm_context.void_pointer) + fun_ptr = cast_to_void_pointer(the_fun.func) if is_closure - ctx_ptr = bit_cast(context.closure_ptr.not_nil!, llvm_context.void_pointer) + ctx_ptr = cast_to_void_pointer(context.closure_ptr.not_nil!) else ctx_ptr = llvm_context.void_pointer.null end @@ -644,9 +655,9 @@ module Crystal last_fun = target_def_fun(node.call.target_def, owner) set_current_debug_location(node) if @debug.line_numbers? - fun_ptr = bit_cast(last_fun, llvm_context.void_pointer) + fun_ptr = cast_to_void_pointer(last_fun.func) if call_self && !owner.metaclass? && !owner.is_a?(LibType) - ctx_ptr = bit_cast(call_self, llvm_context.void_pointer) + ctx_ptr = cast_to_void_pointer(call_self) else ctx_ptr = llvm_context.void_pointer.null end @@ -1128,7 +1139,7 @@ module Crystal # # Making a function that just returns the pointer doesn't work: LLVM inlines it. fun_name = "*#{name}" - thread_local_fun = @main_mod.functions[fun_name]? + thread_local_fun = typed_fun?(@main_mod, fun_name) unless thread_local_fun thread_local_fun = in_main do define_main_function(fun_name, [llvm_type(type).pointer.pointer], llvm_context.void) do |func| @@ -1137,12 +1148,13 @@ module Crystal builder.ret end end - thread_local_fun.add_attribute LLVM::Attribute::NoInline + thread_local_fun.func.add_attribute LLVM::Attribute::NoInline end thread_local_fun = check_main_fun(fun_name, thread_local_fun) - indirection_ptr = alloca llvm_type(type).pointer + pointer_type = llvm_type(type).pointer + indirection_ptr = alloca pointer_type call thread_local_fun, indirection_ptr - load indirection_ptr + load pointer_type, indirection_ptr end def visit(node : TypeDeclaration) @@ -1202,7 +1214,7 @@ module Crystal # Special variables always have an extra pointer already_loaded = (node.special_var? ? false : var.already_loaded) - @last = downcast var.pointer, node.type, var.type, already_loaded + @last = downcast var.pointer, node.type, var.type, already_loaded, extern: false elsif node.name == "self" if node.type.metaclass? @last = type_id(node.type) @@ -1259,7 +1271,7 @@ module Crystal type = type.remove_typedef ivar = type.lookup_instance_var(name) ivar_ptr = instance_var_ptr type, name, value - @last = downcast ivar_ptr, node_type, ivar.type, false + @last = downcast ivar_ptr, node_type, ivar.type, false, extern: type.extern? if type.extern? # When reading the instance variable of a C struct or union # we need to convert C functions to Crystal procs. This @@ -1593,10 +1605,10 @@ module Crystal def check_proc_is_not_closure(value, type) check_fun_name = "~check_proc_is_not_closure" - func = @main_mod.functions[check_fun_name]? || create_check_proc_is_not_closure_fun(check_fun_name) + func = typed_fun?(@main_mod, check_fun_name) || create_check_proc_is_not_closure_fun(check_fun_name) func = check_main_fun check_fun_name, func value = call func, [value] of LLVM::Value - bit_cast value, llvm_proc_type(type) + pointer_cast value, llvm_proc_type(type).pointer end def create_check_proc_is_not_closure_fun(fun_name) @@ -1625,10 +1637,11 @@ module Crystal end def make_fun(type, fun_ptr, ctx_ptr) - closure_ptr = alloca llvm_type(type) - store fun_ptr, gep(closure_ptr, 0, 0) - store ctx_ptr, gep(closure_ptr, 0, 1) - load(closure_ptr) + struct_type = llvm_type(type) + closure_ptr = alloca struct_type + store fun_ptr, aggregate_index(struct_type, closure_ptr, 0) + store ctx_ptr, aggregate_index(struct_type, closure_ptr, 1) + load(struct_type, closure_ptr) end def make_nilable_fun(type) @@ -1643,6 +1656,7 @@ module Crystal old_llvm_context = @llvm_context old_llvm_typer = @llvm_typer old_fun = context.fun + old_fun_type = context.fun_type old_ensure_exception_handlers = @ensure_exception_handlers old_rescue_block = @rescue_block old_catch_pad = @catch_pad @@ -1677,29 +1691,35 @@ module Crystal @alloca_block = old_alloca_block @needs_value = old_needs_value context.fun = old_fun + context.fun_type = old_fun_type set_current_debug_location old_debug_location if @debug.line_numbers? block_value end - def define_main_function(name, arg_types, return_type, needs_alloca = false, &) + def define_main_function(name, arg_types : Array(LLVM::Type), return_type : LLVM::Type, needs_alloca : Bool = false, &) + define_main_function(name, LLVM::Type.function(arg_types, return_type), needs_alloca) { |func| yield func } + end + + def define_main_function(name, type : LLVM::Type, needs_alloca : Bool = false, &) if @llvm_mod != @main_mod raise "wrong usage of define_main_function: you must put it inside an `in_main` block" end - @main_mod.functions.add(name, arg_types, return_type) do |func| - context.fun = func - context.fun.linkage = LLVM::Linkage::Internal if @single_module - if needs_alloca - new_entry_block - yield func - br_from_alloca_to_entry - else - block = func.basic_blocks.append "entry" - position_at_end block - yield func - end + func = add_typed_fun(@main_mod, name, type) + context.fun = func.func + context.fun_type = type + context.fun.linkage = LLVM::Linkage::Internal if @single_module + if needs_alloca + new_entry_block + yield func.func + br_from_alloca_to_entry + else + block = func.func.basic_blocks.append "entry" + position_at_end block + yield func.func end + func end # used for generated internal functions like `~metaclass` and `~match` @@ -1857,20 +1877,19 @@ module Crystal closure_type = @llvm_typer.closure_context_type(closure_vars, parent_closure_type, (self_closured ? current_context.type : nil)) closure_ptr = malloc closure_type closure_vars.each_with_index do |var, i| - current_context.vars[var.name] = LLVMVar.new(gep(closure_ptr, 0, i, var.name), var.type) + current_context.vars[var.name] = LLVMVar.new(gep(closure_type, closure_ptr, 0, i, var.name), var.type) end closure_skip_parent = false if parent_closure_type - store parent_context.not_nil!.closure_ptr.not_nil!, gep(closure_ptr, 0, closure_vars.size, "parent") + store parent_context.not_nil!.closure_ptr.not_nil!, gep(closure_type, closure_ptr, 0, closure_vars.size, "parent") end if self_closured offset = parent_closure_type ? 1 : 0 - self_value = llvm_self - self_value = load self_value if current_context.type.passed_by_value? + self_value = to_rhs(llvm_self, current_context.type) - store self_value, gep(closure_ptr, 0, closure_vars.size + offset, "self") + store self_value, gep(closure_type, closure_ptr, 0, closure_vars.size + offset, "self") current_context.closure_self = current_context.type end @@ -1930,7 +1949,7 @@ module Crystal end def printf(format, args = [] of LLVM::Value) - call @program.printf(@llvm_mod, llvm_context), [builder.global_string_pointer(format)] + args + call c_printf_fun, [builder.global_string_pointer(format)] + args end # Emits a debug message that shows the current llvm basic block name, @@ -1972,28 +1991,35 @@ module Crystal def allocate_aggregate(type) struct_type = llvm_struct_type(type) if type.passed_by_value? - @last = alloca struct_type + type_ptr = alloca struct_type else if type.is_a?(InstanceVarContainer) && !type.struct? && type.all_instance_vars.each_value.any? &.type.has_inner_pointers? - @last = malloc struct_type + type_ptr = malloc struct_type else - @last = malloc_atomic struct_type + type_ptr = malloc_atomic struct_type end end - memset @last, int8(0), struct_type.size - type_ptr = @last + + memset type_ptr, int8(0), struct_type.size run_instance_vars_initializers(type, type, type_ptr) + + unless type.struct? + type_id_ptr = aggregate_index(struct_type, type_ptr, 0) + store type_id(type), type_id_ptr + end + @last = type_ptr end def allocate_tuple(type, &) - struct_type = alloca llvm_type(type) + struct_type = llvm_type(type) + tuple = alloca struct_type type.tuple_types.each_with_index do |tuple_type, i| exp_type, value = yield tuple_type, i - assign aggregate_index(struct_type, i), tuple_type, exp_type, value + assign aggregate_index(struct_type, tuple, i), tuple_type, exp_type, value end - struct_type + tuple end def run_instance_vars_initializers(real_type, type : ClassType | GenericClassInstanceType, type_ptr) @@ -2047,7 +2073,7 @@ module Crystal pointer = call_c_malloc size end - bit_cast pointer, type.pointer + pointer_cast pointer, type.pointer end def array_malloc(type, count) @@ -2068,11 +2094,11 @@ module Crystal end memset pointer, int8(0), size - bit_cast pointer, type.pointer + pointer_cast pointer, type.pointer end def crystal_malloc_fun - @malloc_fun ||= @main_mod.functions[MALLOC_NAME]? + @malloc_fun ||= typed_fun?(@main_mod, MALLOC_NAME) if malloc_fun = @malloc_fun check_main_fun MALLOC_NAME, malloc_fun else @@ -2081,7 +2107,7 @@ module Crystal end def crystal_malloc_atomic_fun - @malloc_atomic_fun ||= @main_mod.functions[MALLOC_ATOMIC_NAME]? + @malloc_atomic_fun ||= typed_fun?(@main_mod, MALLOC_ATOMIC_NAME) if malloc_fun = @malloc_atomic_fun check_main_fun MALLOC_ATOMIC_NAME, malloc_fun else @@ -2090,7 +2116,7 @@ module Crystal end def crystal_realloc_fun - @realloc_fun ||= @main_mod.functions[REALLOC_NAME]? + @realloc_fun ||= typed_fun?(@main_mod, REALLOC_NAME) if realloc_fun = @realloc_fun check_main_fun REALLOC_NAME, realloc_fun else @@ -2099,7 +2125,7 @@ module Crystal end def crystal_raise_overflow_fun - @raise_overflow_fun ||= @main_mod.functions[RAISE_OVERFLOW_NAME]? + @raise_overflow_fun ||= typed_fun?(@main_mod, RAISE_OVERFLOW_NAME) if raise_overflow_fun = @raise_overflow_fun check_main_fun RAISE_OVERFLOW_NAME, raise_overflow_fun else @@ -2117,9 +2143,9 @@ module Crystal end def c_malloc_fun - malloc_fun = @c_malloc_fun = @main_mod.functions["malloc"]? || begin + malloc_fun = @c_malloc_fun = fetch_typed_fun(@main_mod, "malloc") do size = @program.bits64? ? @main_llvm_context.int64 : @main_llvm_context.int32 - @main_mod.functions.add("malloc", ([size]), @main_llvm_context.void_pointer) + LLVM::Type.function([size], @main_llvm_context.void_pointer) end check_main_fun "malloc", malloc_fun @@ -2131,9 +2157,9 @@ module Crystal end def c_realloc_fun - realloc_fun = @c_realloc_fun = @main_mod.functions["realloc"]? || begin + realloc_fun = @c_realloc_fun = fetch_typed_fun(@main_mod, "realloc") do size = @program.bits64? ? @main_llvm_context.int64 : @main_llvm_context.int32 - @main_mod.functions.add("realloc", ([@main_llvm_context.void_pointer, size]), @main_llvm_context.void_pointer) + LLVM::Type.function([@main_llvm_context.void_pointer, size], @main_llvm_context.void_pointer) end check_main_fun "realloc", realloc_fun @@ -2143,15 +2169,14 @@ module Crystal len_arg = @program.bits64? ? size : trunc(size, llvm_context.int32) pointer = cast_to_void_pointer pointer - res = call @program.memset(@llvm_mod, llvm_context), [pointer, value, len_arg, int1(0)] - + res = call c_memset_fun, [pointer, value, len_arg, int1(0)] LibLLVM.set_instr_param_alignment(res, 1, 4) res end def memcpy(dest, src, len, align, volatile) - res = call @program.memcpy(@llvm_mod, llvm_context), [dest, src, len, volatile] + res = call c_memcpy_fun, [dest, src, len, volatile] LibLLVM.set_instr_param_alignment(res, 1, align) LibLLVM.set_instr_param_alignment(res, 2, align) @@ -2167,21 +2192,66 @@ module Crystal end end + private def c_printf_fun + fetch_typed_fun(@llvm_mod, "printf") do + LLVM::Type.function([@llvm_context.void_pointer], @llvm_context.int32, true) + end + end + + private def c_memset_fun + name = {% if LibLLVM::IS_LT_150 %} + @program.bits64? ? "llvm.memset.p0i8.i64" : "llvm.memset.p0i8.i32" + {% else %} + @program.bits64? ? "llvm.memset.p0.i64" : "llvm.memset.p0.i32" + {% end %} + + fetch_typed_fun(@llvm_mod, name) do + len_type = @program.bits64? ? @llvm_context.int64 : @llvm_context.int32 + arg_types = [@llvm_context.void_pointer, @llvm_context.int8, len_type, @llvm_context.int1] + LLVM::Type.function(arg_types, @llvm_context.void) + end + end + + private def c_memcpy_fun + name = {% if LibLLVM::IS_LT_150 %} + @program.bits64? ? "llvm.memcpy.p0i8.p0i8.i64" : "llvm.memcpy.p0i8.p0i8.i32" + {% else %} + @program.bits64? ? "llvm.memcpy.p0.p0.i64" : "llvm.memcpy.p0.p0.i32" + {% end %} + + fetch_typed_fun(@llvm_mod, name) do + len_type = @program.bits64? ? @llvm_context.int64 : @llvm_context.int32 + arg_types = [@llvm_context.void_pointer, @llvm_context.void_pointer, len_type, @llvm_context.int1] + LLVM::Type.function(arg_types, @llvm_context.void) + end + end + def to_lhs(value, type) - type.passed_by_value? ? value : load value + # `llvm_embedded_type` needed for void-like types + type.passed_by_value? ? value : load(llvm_embedded_type(type), value) end def to_rhs(value, type) - type.passed_by_value? ? load value : value + type.passed_by_value? ? load(llvm_embedded_type(type), value) : value + end + + def extern_to_lhs(value, type) + type.passed_by_value? ? value : load(llvm_embedded_c_type(type), value) end - def aggregate_index(ptr, index) - gep ptr, 0, index + def extern_to_rhs(value, type) + type.passed_by_value? ? load(llvm_embedded_c_type(type), value) : value + end + + # *type* is the pointee type of *ptr* (not the type of the returned + # element) + def aggregate_index(type : LLVM::Type, ptr : LLVM::Value, index : Int32) + gep type, ptr, 0, index end def instance_var_ptr(type, name, pointer) if type.extern_union? - return union_field_ptr(type.instance_vars[name].type, pointer) + return union_field_ptr(type, type.instance_vars[name].type, pointer) end index = type.index_of_instance_var(name).not_nil! @@ -2190,21 +2260,24 @@ module Crystal index += 1 end + target_type = type if type.is_a?(VirtualType) if type.struct? if (_type = type.remove_indirection).is_a?(UnionType) # For a struct we need to cast the second part of the union to the base type _, value_ptr = union_type_and_value_pointer(pointer, _type) - pointer = bit_cast value_ptr, llvm_type(type.base_type).pointer + target_type = type.base_type + pointer = cast_to_pointer value_ptr, target_type else # Nothing, there's only one subclass so it's the struct already end else - pointer = cast_to pointer, type.base_type + target_type = type.base_type + pointer = cast_to pointer, target_type end end - aggregate_index pointer, index + aggregate_index llvm_struct_type(target_type), pointer, index end def process_finished_hooks @@ -2284,38 +2357,6 @@ module Crystal name end end - - class Program - def printf(llvm_mod, llvm_context) - llvm_mod.functions["printf"]? || llvm_mod.functions.add("printf", [llvm_context.void_pointer], llvm_context.int32, true) - end - - def realloc(llvm_mod, llvm_context) - llvm_mod.functions["realloc"]? || llvm_mod.functions.add("realloc", ([llvm_context.void_pointer, llvm_context.int64]), llvm_context.void_pointer) - end - - def memset(llvm_mod, llvm_context) - name = bits64? ? "llvm.memset.p0i8.i64" : "llvm.memset.p0i8.i32" - len_type = bits64? ? llvm_context.int64 : llvm_context.int32 - - llvm_mod.functions[name]? || begin - arg_types = [llvm_context.void_pointer, llvm_context.int8, len_type, llvm_context.int1] - - llvm_mod.functions.add(name, arg_types, llvm_context.void) - end - end - - def memcpy(llvm_mod, llvm_context) - name = bits64? ? "llvm.memcpy.p0i8.p0i8.i64" : "llvm.memcpy.p0i8.p0i8.i32" - len_type = bits64? ? llvm_context.int64 : llvm_context.int32 - - llvm_mod.functions[name]? || begin - arg_types = [llvm_context.void_pointer, llvm_context.void_pointer, len_type, llvm_context.int1] - - llvm_mod.functions.add(name, arg_types, llvm_context.void) - end - end - end end require "./*" diff --git a/src/compiler/crystal/codegen/cond.cr b/src/compiler/crystal/codegen/cond.cr index 900aa7636b8a..8394af9ad157 100644 --- a/src/compiler/crystal/codegen/cond.cr +++ b/src/compiler/crystal/codegen/cond.cr @@ -44,7 +44,7 @@ class Crystal::CodeGenVisitor end if has_bool - value = load(bit_cast value_ptr, llvm_context.int1.pointer) + value = load(llvm_context.int1, pointer_cast value_ptr, llvm_context.int1.pointer) is_bool = equal? type_id, type_id(@program.bool) cond = and cond, not(and(is_bool, not(value))) end @@ -54,7 +54,7 @@ class Crystal::CodeGenVisitor next unless union_type.is_a?(PointerInstanceType) is_pointer = equal? type_id, type_id(union_type) - pointer_value = load(bit_cast value_ptr, llvm_type(union_type).pointer) + pointer_value = load(llvm_type(union_type), cast_to_pointer(value_ptr, union_type)) pointer_null = null_pointer?(pointer_value) cond = and cond, not(and(is_pointer, pointer_null)) end diff --git a/src/compiler/crystal/codegen/const.cr b/src/compiler/crystal/codegen/const.cr index 3bfd61652093..88a50022d54d 100644 --- a/src/compiler/crystal/codegen/const.cr +++ b/src/compiler/crystal/codegen/const.cr @@ -79,7 +79,7 @@ class Crystal::CodeGenVisitor const_type = const.value.type if const_type.passed_by_value? - @last = load @last + @last = load llvm_type(const_type), @last end global.initializer = @last @@ -108,7 +108,7 @@ class Crystal::CodeGenVisitor const_type = const.value.type if const_type.passed_by_value? - @last = load @last + @last = load llvm_type(const_type), @last end store @last, global @@ -128,7 +128,7 @@ class Crystal::CodeGenVisitor return global if const.initializer init_function_name = "~#{const.initialized_llvm_name}" - func = @main_mod.functions[init_function_name]? || create_initialize_const_function(init_function_name, const) + func = typed_fun?(@main_mod, init_function_name) || create_initialize_const_function(init_function_name, const) func = check_main_fun init_function_name, func set_current_debug_location const.locations.try &.first? if @debug.line_numbers? @@ -159,21 +159,21 @@ class Crystal::CodeGenVisitor request_value(const.value) - if const.value.type.passed_by_value? - @last = load @last + const_type = const.value.type + if const_type.passed_by_value? + @last = load llvm_type(const_type), @last end if @last.constant? global.initializer = @last global.global_constant = true - const_type = const.value.type if const_type.is_a?(PrimitiveType) || const_type.is_a?(EnumType) const.initializer = @last end else - global.initializer = llvm_type(const.value.type).null - unless const.value.type.nil_type? || const.value.type.void? + global.initializer = llvm_type(const_type).null + unless const_type.nil_type? || const_type.void? store @last, global end end @@ -223,7 +223,7 @@ class Crystal::CodeGenVisitor end read_function_name = "~#{const.llvm_name}:read" - func = @main_mod.functions[read_function_name]? || create_read_const_function(read_function_name, const) + func = typed_fun?(@main_mod, read_function_name) || create_read_const_function(read_function_name, const) func = check_main_fun read_function_name, func call func end diff --git a/src/compiler/crystal/codegen/context.cr b/src/compiler/crystal/codegen/context.cr index 595306476b34..c418822974ec 100644 --- a/src/compiler/crystal/codegen/context.cr +++ b/src/compiler/crystal/codegen/context.cr @@ -3,6 +3,7 @@ require "./codegen" class Crystal::CodeGenVisitor class Context property fun : LLVM::Function + property fun_type : LLVM::Type property fun_debug_params = [] of LibLLVM::MetadataRef property type : Type property vars : Hash(String, LLVMVar) @@ -20,7 +21,7 @@ class Crystal::CodeGenVisitor property closure_parent_context : Context? property closure_self : Type? - def initialize(@fun, @type, @vars = LLVMVars.new) + def initialize(@fun, @fun_type, @type, @vars = LLVMVars.new) @closure_skip_parent = false end @@ -37,7 +38,7 @@ class Crystal::CodeGenVisitor end def clone - context = Context.new @fun, @type, @vars + context = Context.new @fun, @fun_type, @type, @vars context.return_type = @return_type context.return_phi = @return_phi context.break_phi = @break_phi diff --git a/src/compiler/crystal/codegen/crystal_llvm_builder.cr b/src/compiler/crystal/codegen/crystal_llvm_builder.cr index f635d34f3cfb..55ad6f49735a 100644 --- a/src/compiler/crystal/codegen/crystal_llvm_builder.cr +++ b/src/compiler/crystal/codegen/crystal_llvm_builder.cr @@ -2,7 +2,7 @@ module Crystal class CrystalLLVMBuilder property end : Bool - def initialize(@builder : LLVM::Builder, @llvm_typer : LLVMTyper, @printf : LLVM::Function) + def initialize(@builder : LLVM::Builder, @llvm_typer : LLVMTyper, @printf : LLVMTypedFunction) @end = false end diff --git a/src/compiler/crystal/codegen/debug.cr b/src/compiler/crystal/codegen/debug.cr index 6124840113c3..a363e994d1dd 100644 --- a/src/compiler/crystal/codegen/debug.cr +++ b/src/compiler/crystal/codegen/debug.cr @@ -492,7 +492,7 @@ module Crystal debug_alloca = alloca alloca.type, "dbg.#{arg_name}" store alloca, debug_alloca declare_parameter(arg_name, arg_type, arg_no, debug_alloca, location) - alloca = load debug_alloca + alloca = load alloca.type, debug_alloca set_current_debug_location old_debug_location alloca end diff --git a/src/compiler/crystal/codegen/exception.cr b/src/compiler/crystal/codegen/exception.cr index 96e4598ce7da..9a33e1337550 100644 --- a/src/compiler/crystal/codegen/exception.cr +++ b/src/compiler/crystal/codegen/exception.cr @@ -62,7 +62,7 @@ class Crystal::CodeGenVisitor windows = @program.has_flag? "windows" - context.fun.personality_function = windows_personality_fun if windows + context.fun.personality_function = windows_personality_fun.func if windows # This is the block which is entered when the body raises an exception rescue_block = new_block "rescue" @@ -122,7 +122,9 @@ class Crystal::CodeGenVisitor position_at_end catch_body # Allocate space for the caught exception - caught_exception_ptr = alloca llvm_type(@program.exception.virtual_type) + exception_type = @program.exception.virtual_type + exception_llvm_type = llvm_type(exception_type) + caught_exception_ptr = alloca exception_llvm_type # The catchpad instruction dictates which types of exceptions this block handles, # we want all of them, so we rescue all void* by passing the void_ptr_type_descriptor. @@ -133,22 +135,25 @@ class Crystal::CodeGenVisitor # builder.printf("catchpad entered #{node.location}\n", catch_pad: @catch_pad) - caught_exception = load caught_exception_ptr - exception_type_id = type_id(caught_exception, @program.exception.virtual_type) + caught_exception = load exception_llvm_type, caught_exception_ptr + exception_type_id = type_id(caught_exception, exception_type) else # Unwind exception handling code - used on non-windows platforms - is a lot simpler. # First we generate the landing pad instruction, this returns a tuple of the libunwind # exception object and the type ID of the exception. This tuple is set up in the crystal # personality function in raise.cr lp_ret_type = llvm_typer.landing_pad_type - lp = builder.landing_pad lp_ret_type, main_fun(personality_name), [] of LLVM::Value + lp = builder.landing_pad lp_ret_type, main_fun(personality_name).func, [] of LLVM::Value unwind_ex_obj = extract_value lp, 0 exception_type_id = extract_value lp, 1 # We call __crystal_get_exception to get the actual crystal `Exception` object. get_exception_fun = main_fun(GET_EXCEPTION_NAME) + get_exception_arg_type = get_exception_fun.type.params_types.first # Void* or LibUnwind::Exception* + get_exception_arg = pointer_cast(unwind_ex_obj, get_exception_arg_type) + set_current_debug_location node if @debug.line_numbers? - caught_exception_ptr = call get_exception_fun, [bit_cast(unwind_ex_obj, get_exception_fun.params.first.type)] + caught_exception_ptr = call get_exception_fun, [get_exception_arg] caught_exception = int2ptr caught_exception_ptr, llvm_typer.type_id_pointer end @@ -253,7 +258,7 @@ class Crystal::CodeGenVisitor @catch_pad = builder.catch_pad catch_switch, [void_ptr_type_descriptor, int32(0), llvm_context.void_pointer.null] else lp_ret_type = llvm_typer.landing_pad_type - lp = builder.landing_pad lp_ret_type, main_fun(personality_name), [] of LLVM::Value + lp = builder.landing_pad lp_ret_type, main_fun(personality_name).func, [] of LLVM::Value unwind_ex_obj = extract_value lp, 0 end @@ -284,7 +289,9 @@ class Crystal::CodeGenVisitor unreachable else raise_fun = main_fun(RAISE_NAME) - codegen_call_or_invoke(node, nil, nil, raise_fun, [bit_cast(unwind_ex_obj.not_nil!, raise_fun.params.first.type)], true, @program.no_return) + raise_fun_arg_type = raise_fun.func.params.first.type # Void* or LibUnwind::Exception* + raise_fun_arg = pointer_cast(unwind_ex_obj.not_nil!, raise_fun_arg_type) + codegen_call_or_invoke(node, nil, nil, raise_fun, [raise_fun_arg], true, @program.no_return) end end @@ -310,14 +317,14 @@ class Crystal::CodeGenVisitor end private def windows_throw_fun - @llvm_mod.functions["_CxxThrowException"]? || begin - @llvm_mod.functions.add("_CxxThrowException", [llvm_context.void_pointer, llvm_context.void_pointer], llvm_context.void, false) + fetch_typed_fun(@llvm_mod, "_CxxThrowException") do + LLVM::Type.function([@llvm_context.void_pointer, @llvm_context.void_pointer], @llvm_context.void, false) end end private def windows_personality_fun - @llvm_mod.functions["__CxxFrameHandler3"]? || begin - @llvm_mod.functions.add("__CxxFrameHandler3", [] of LLVM::Type, llvm_context.int32, true) + fetch_typed_fun(@llvm_mod, "__CxxFrameHandler3") do + LLVM::Type.function([] of LLVM::Type, @llvm_context.int32, true) end end end diff --git a/src/compiler/crystal/codegen/fun.cr b/src/compiler/crystal/codegen/fun.cr index a41a980c01b2..9a8e51df3175 100644 --- a/src/compiler/crystal/codegen/fun.cr +++ b/src/compiler/crystal/codegen/fun.cr @@ -1,49 +1,60 @@ require "./codegen" class Crystal::CodeGenVisitor - def target_def_fun(target_def, self_type) : LLVM::Function + def typed_fun?(mod : LLVM::Module, name : String) : LLVMTypedFunction? + if func = mod.functions[name]? + LLVMTypedFunction.new(@fun_types[{mod, name}], func) + end + end + + def add_typed_fun(mod : LLVM::Module, name : String, type : LLVM::Type) : LLVMTypedFunction + func = mod.functions.add(name, type) + @fun_types[{mod, name}] = type + LLVMTypedFunction.new(type, func) + end + + def fetch_typed_fun(mod : LLVM::Module, name : String, & : -> LLVM::Type) : LLVMTypedFunction + typed_fun?(mod, name) || add_typed_fun(mod, name, yield) + end + + def target_def_fun(target_def, self_type) : LLVMTypedFunction mangled_name = target_def.mangled_name(@program, self_type) self_type_mod = type_module(self_type).mod - func = self_type_mod.functions[mangled_name]? || codegen_fun(mangled_name, target_def, self_type) + func = typed_fun?(self_type_mod, mangled_name) || codegen_fun(mangled_name, target_def, self_type) check_mod_fun self_type_mod, mangled_name, func end def main_fun(name) - func = @main_mod.functions[name]? - unless func - raise "BUG: #{name} is not defined" - end - + func = typed_fun?(@main_mod, name) || raise "BUG: #{name} is not defined" check_main_fun name, func end - def check_main_fun(name, func) + def check_main_fun(name, func : LLVMTypedFunction) : LLVMTypedFunction check_mod_fun @main_mod, name, func end - def check_mod_fun(mod, name, func) - return func if @llvm_mod == mod - @llvm_mod.functions[name]? || declare_fun(name, func) + def check_mod_fun(mod, name, func : LLVMTypedFunction) : LLVMTypedFunction + if @llvm_mod == mod + func + elsif existing_func = @llvm_mod.functions[name]? + LLVMTypedFunction.new(@fun_types[{@llvm_mod, name}], existing_func) + else + declare_fun(name, func) + end end - def declare_fun(mangled_name, func) - param_types = @llvm_typer.copy_types(func.params.types) - return_type = @llvm_typer.copy_type(func.return_type) - - new_fun = @llvm_mod.functions.add( - mangled_name, - param_types, - return_type, - func.varargs? - ) + def declare_fun(mangled_name, func : LLVMTypedFunction) : LLVMTypedFunction + type = @llvm_typer.copy_type(func.type) + typed_fun = add_typed_fun(@llvm_mod, mangled_name, type) - func.params.to_a.each_with_index do |p1, index| + new_fun = typed_fun.func + func.func.params.to_a.each_with_index do |p1, index| attrs = new_fun.attributes(index + 1) new_fun.add_attribute(attrs, index + 1) unless attrs.value == 0 end - new_fun + typed_fun end def codegen_fun(mangled_name, target_def, self_type, is_exported_fun = false, fun_module_info = type_module(self_type), is_fun_literal = false, is_closure = false) @@ -84,7 +95,12 @@ class Crystal::CodeGenVisitor emit_def_debug_metadata target_def unless @debug.none? set_current_debug_location target_def if @debug.line_numbers? - context.fun.add_attribute LLVM::Attribute::UWTable + {% if LibLLVM::IS_LT_150 %} + context.fun.add_attribute LLVM::Attribute::UWTable + {% else %} + context.fun.add_attribute LLVM::Attribute::UWTable, value: @program.has_flag?("aarch64") ? LLVM::UWTableKind::Sync : LLVM::UWTableKind::Async + {% end %} + if @program.has_flag?("darwin") # Disable frame pointer elimination in Darwin, as it causes issues during stack unwind context.fun.add_target_dependent_attribute "frame-pointer", "all" @@ -94,7 +110,10 @@ class Crystal::CodeGenVisitor if is_closure clear_current_debug_location if @debug.line_numbers? - setup_closure_vars target_def.vars, context.closure_vars.not_nil! + void_ptr = context.fun.params.first + closure_type = llvm_typer.copy_type(context.closure_type.not_nil!) + closure_ptr = pointer_cast void_ptr, closure_type.pointer + setup_closure_vars target_def.vars, context.closure_vars.not_nil!, self.context, closure_type, closure_ptr else context.reset_closure end @@ -107,7 +126,7 @@ class Crystal::CodeGenVisitor # In the case of a closure proc literal (-> { ... }), the closure_ptr is not # the one of the parent context, it's the last parameter of this proc literal. closure_parent_context = old_context.clone - closure_parent_context.closure_ptr = fun_literal_closure_ptr + closure_parent_context.closure_ptr = closure_ptr.not_nil! context.closure_parent_context = closure_parent_context end @@ -151,7 +170,7 @@ class Crystal::CodeGenVisitor # (because the closure data is a pointer) and so we must load the # real value first. if args.first.type.is_a?(PrimitiveType) - primitive_params[0] = load(primitive_params[0]) + primitive_params[0] = load(llvm_type(args.first.type), primitive_params[0]) end codegen_primitive(nil, body, target_def, primitive_params) @@ -205,7 +224,7 @@ class Crystal::CodeGenVisitor end end - context.fun + LLVMTypedFunction.new(context.fun_type, context.fun) end end @@ -216,14 +235,14 @@ class Crystal::CodeGenVisitor abi_info = abi_info(target_def) ret_type = abi_info.return_type if cast = ret_type.cast - casted_last = bit_cast @last, cast.pointer - last = load casted_last + casted_last = pointer_cast @last, cast.pointer + last = load cast, casted_last ret last return end if (attr = ret_type.attr) && attr == LLVM::Attribute::StructRet - store load(@last), context.fun.params[0] + store load(llvm_type(target_def.body.type), @last), context.fun.params[0] ret return end @@ -283,7 +302,12 @@ class Crystal::CodeGenVisitor end llvm_arg_type end - llvm_return_type = llvm_return_type(target_def.type) + + llvm_return_type = {% if LibLLVM::IS_LT_150 %} + llvm_return_type(target_def.type) + {% else %} + target_def.llvm_intrinsic? ? llvm_intrinsic_return_type(target_def.type) : llvm_return_type(target_def.type) + {% end %} if is_closure llvm_args_types.insert(0, llvm_context.void_pointer) @@ -321,8 +345,9 @@ class Crystal::CodeGenVisitor # This is the case where we declared a fun that was not used and now we # are defining its body. - if existing_fun = @llvm_mod.functions[mangled_name]? - context.fun = existing_fun + if existing_fun = typed_fun?(@llvm_mod, mangled_name) + context.fun = existing_fun.func + context.fun_type = existing_fun.type return args end @@ -400,7 +425,10 @@ class Crystal::CodeGenVisitor end def setup_context_fun(mangled_name, target_def, llvm_args_types, llvm_return_type) : Nil - context.fun = @llvm_mod.functions.add(mangled_name, llvm_args_types, llvm_return_type, target_def.varargs?) + fun_type = LLVM::Type.function(llvm_args_types, llvm_return_type, target_def.varargs?) + typed_fun = add_typed_fun(@llvm_mod, mangled_name, fun_type) + context.fun = typed_fun.func + context.fun_type = typed_fun.type if @debug.variables? context.fun.add_attribute LLVM::Attribute::NoInline @@ -418,10 +446,10 @@ class Crystal::CodeGenVisitor end end - def setup_closure_vars(def_vars, closure_vars, context = self.context, closure_ptr = fun_literal_closure_ptr) + def setup_closure_vars(def_vars, closure_vars, context, closure_type, closure_ptr) if context.closure_skip_parent parent_context = context.closure_parent_context.not_nil! - setup_closure_vars(def_vars, parent_context.closure_vars.not_nil!, parent_context, closure_ptr) + setup_closure_vars(def_vars, parent_context.closure_vars.not_nil!, parent_context, closure_type, closure_ptr) else closure_vars.each_with_index do |var, i| # A closured var in this context might have the same name as @@ -431,27 +459,24 @@ class Crystal::CodeGenVisitor def_var = def_vars.try &.[var.name]? next if def_var && !def_var.closured? - self.context.vars[var.name] = LLVMVar.new(gep(closure_ptr, 0, i, var.name), var.type) + self.context.vars[var.name] = LLVMVar.new(gep(closure_type, closure_ptr, 0, i, var.name), var.type) end if (closure_parent_context = context.closure_parent_context) && (parent_vars = closure_parent_context.closure_vars) - parent_closure_ptr = gep(closure_ptr, 0, closure_vars.size, "parent_ptr") - setup_closure_vars(def_vars, parent_vars, closure_parent_context, load(parent_closure_ptr, "parent")) + parent_closure_type = llvm_typer.copy_type(closure_parent_context.closure_type.not_nil!) + parent_closure_ptr = gep(closure_type, closure_ptr, 0, closure_vars.size, "parent_ptr") + parent_closure = load(parent_closure_type.pointer, parent_closure_ptr, "parent") + setup_closure_vars(def_vars, parent_vars, closure_parent_context, parent_closure_type, parent_closure) elsif closure_self = context.closure_self offset = context.closure_parent_context ? 1 : 0 - self_value = gep(closure_ptr, 0, closure_vars.size + offset, "self") - self_value = load(self_value) unless context.type.passed_by_value? + self_value = gep(closure_type, closure_ptr, 0, closure_vars.size + offset, "self") + self_value = load(llvm_type(closure_self), self_value) unless context.type.passed_by_value? self.context.vars["self"] = LLVMVar.new(self_value, closure_self, true) end end end - def fun_literal_closure_ptr - void_ptr = context.fun.params.first - bit_cast void_ptr, llvm_typer.copy_type(context.closure_type.not_nil!).pointer - end - def create_local_copy_of_fun_args(target_def, self_type, args, is_fun_literal, is_closure) offset = is_closure ? 1 : 0 @@ -515,7 +540,7 @@ class Crystal::CodeGenVisitor context.vars[arg.name] = LLVMVar.new(value, var_type) else pointer = alloca(llvm_type(var_type), arg.name) - casted_pointer = bit_cast pointer, value.type.pointer + casted_pointer = pointer_cast pointer, value.type.pointer store value, casted_pointer pointer = declare_debug_for_function_argument(arg.name, var_type, index + 1, pointer, location) unless target_def.naked? context.vars[arg.name] = LLVMVar.new(pointer, var_type) @@ -539,8 +564,8 @@ class Crystal::CodeGenVisitor pointer = declare_debug_for_function_argument(arg.name, var_type, index + 1, pointer, location) unless target_def.naked? if fun_proc - value = bit_cast(value, llvm_context.void_pointer) - value = make_fun(var_type, value, llvm_context.void_pointer.null) + fun_ptr = cast_to_void_pointer(value) + value = make_fun(var_type, fun_ptr, llvm_context.void_pointer.null) end context.vars[arg.name] = LLVMVar.new(pointer, var_type) diff --git a/src/compiler/crystal/codegen/llvm_builder_helper.cr b/src/compiler/crystal/codegen/llvm_builder_helper.cr index 687b60d33b10..088ea772552e 100644 --- a/src/compiler/crystal/codegen/llvm_builder_helper.cr +++ b/src/compiler/crystal/codegen/llvm_builder_helper.cr @@ -1,4 +1,6 @@ module Crystal + record LLVMTypedFunction, type : LLVM::Type, func : LLVM::Function + module LLVMBuilderHelper def int1(n) llvm_context.int1.const_int(n) @@ -79,48 +81,64 @@ module Crystal builder.icmp LLVM::IntPredicate::NE, value, value.type.null end - def gep(ptr, index0 : Int32, name = "") + def gep(ptr : LLVM::Value, index0 : Int32, name = "") gep ptr, int32(index0), name end - def gep(ptr, index0 : LLVM::Value, name = "") + def gep(ptr : LLVM::Value, index0 : LLVM::Value, name = "") builder.inbounds_gep ptr, index0, name end - def gep(ptr, index0 : Int32, index1 : Int32, name = "") + def gep(ptr : LLVM::Value, index0 : Int32, index1 : Int32, name = "") gep ptr, int32(index0), int32(index1), name end - def gep(ptr, index0 : LLVM::Value, index1 : LLVM::Value, name = "") + def gep(ptr : LLVM::Value, index0 : LLVM::Value, index1 : LLVM::Value, name = "") builder.inbounds_gep ptr, index0, index1, name end - def call(func, name : String = "") + def gep(type : LLVM::Type, ptr : LLVM::Value, index0 : Int32, name = "") + gep type, ptr, int32(index0), name + end + + def gep(type : LLVM::Type, ptr : LLVM::Value, index0 : LLVM::Value, name = "") + builder.inbounds_gep type, ptr, index0, name + end + + def gep(type : LLVM::Type, ptr : LLVM::Value, index0 : Int32, index1 : Int32, name = "") + gep type, ptr, int32(index0), int32(index1), name + end + + def gep(type : LLVM::Type, ptr : LLVM::Value, index0 : LLVM::Value, index1 : LLVM::Value, name = "") + builder.inbounds_gep type, ptr, index0, index1, name + end + + def call(func : LLVMTypedFunction, name : String = "") call(func, [] of LLVM::Value, name) end - def call(func, arg : LLVM::Value, name : String = "") + def call(func : LLVMTypedFunction, arg : LLVM::Value, name : String = "") call(func, [arg], name) end - def call(func : LLVM::Function, args : Array(LLVM::Value), name : String = "") + def call(func : LLVMTypedFunction, args : Array(LLVM::Value), name : String = "") if catch_pad = @catch_pad funclet = builder.build_operand_bundle_def("funclet", [catch_pad]) else funclet = LLVM::OperandBundleDef.null end - builder.call(func, args, bundle: funclet, name: name) + builder.call(func.type, func.func, args, bundle: funclet, name: name) end - def invoke(func : LLVM::Function, args : Array(LLVM::Value), a_then, a_catch, name : String = "") + def invoke(func : LLVMTypedFunction, args : Array(LLVM::Value), a_then, a_catch, name : String = "") if catch_pad = @catch_pad funclet = builder.build_operand_bundle_def("funclet", [catch_pad]) else funclet = LLVM::OperandBundleDef.null end - builder.invoke(func, args, a_then, a_catch, bundle: funclet, name: name) + builder.invoke(func.type, func.func, args, a_then, a_catch, bundle: funclet, name: name) end delegate ptr2int, int2ptr, and, or, not, bit_cast, @@ -139,10 +157,6 @@ module Crystal builder.ret value end - def cast_to_void_pointer(pointer) - bit_cast pointer, llvm_context.void_pointer - end - def extend_int(from_type, to_type, value) from_type.signed? ? builder.sext(value, llvm_type(to_type)) : builder.zext(value, llvm_type(to_type)) end @@ -171,16 +185,32 @@ module Crystal end end - def cast_to(value, type) - bit_cast value, llvm_type(type) + def cast_to(value : LLVM::ValueMethods, type : Type) + pointer_cast value, llvm_type(type) + end + + def cast_to_pointer(value : LLVM::ValueMethods, type : Type) + pointer_cast value, llvm_type(type).pointer + end + + def cast_to_void_pointer(pointer : LLVM::ValueMethods) + pointer_cast pointer, llvm_context.void_pointer end - def cast_to_pointer(value, type) - bit_cast value, llvm_type(type).pointer + # *type* must be a pointer type; on LLVM 15.0 or above *type* is not + # evaluated at all and *value* is returned unchanged, because all opaque + # pointer types (in the same context) are identical + macro pointer_cast(value, type) + {% if LibLLVM::IS_LT_150 %} + bit_cast({{ value }}, {{ type }}) + {% else %} + {{ value }} + {% end %} end delegate llvm_type, llvm_struct_type, llvm_arg_type, llvm_embedded_type, - llvm_c_type, llvm_c_return_type, llvm_return_type, to: llvm_typer + llvm_c_type, llvm_c_return_type, llvm_return_type, llvm_embedded_c_type, + llvm_intrinsic_return_type, to: llvm_typer def llvm_proc_type(type) llvm_typer.proc_type(type.as(ProcInstanceType)) diff --git a/src/compiler/crystal/codegen/llvm_typer.cr b/src/compiler/crystal/codegen/llvm_typer.cr index 1563c7b502c0..8b2cfdf65ec4 100644 --- a/src/compiler/crystal/codegen/llvm_typer.cr +++ b/src/compiler/crystal/codegen/llvm_typer.cr @@ -405,7 +405,7 @@ module Crystal end def llvm_embedded_c_type(type : ProcInstanceType, wants_size = false) - proc_type(type) + proc_type(type).pointer end def llvm_embedded_c_type(type, wants_size = false) @@ -413,11 +413,11 @@ module Crystal end def llvm_c_type(type : ProcInstanceType) - proc_type(type) + proc_type(type).pointer end def llvm_c_type(type : NilableProcType) - proc_type(type.proc_type) + proc_type(type.proc_type).pointer end def llvm_c_type(type : TupleInstanceType) @@ -458,15 +458,31 @@ module Crystal llvm_type(type) end + # Since LLVM 15, LLVM intrinsics must return unnamed structs, and instances + # of the named `Tuple` struct are no longer substitutable + # This happens when binding to intrinsics like `llvm.sadd.with.overflow.*` + # as lib funs directly + def llvm_intrinsic_return_type(type : TupleInstanceType) + @llvm_context.struct(type.tuple_types.map { |tuple_type| llvm_embedded_type(tuple_type).as(LLVM::Type) }) + end + + def llvm_intrinsic_return_type(type : NamedTupleInstanceType) + @llvm_context.struct(type.entries.map { |entry| llvm_embedded_type(entry.type).as(LLVM::Type) }) + end + + def llvm_intrinsic_return_type(type : Type) + llvm_return_type(type) + end + def closure_type(type : ProcInstanceType) arg_types = type.arg_types.map { |arg_type| llvm_type(arg_type) } arg_types.insert(0, @llvm_context.void_pointer) - LLVM::Type.function(arg_types, llvm_type(type.return_type)).pointer + LLVM::Type.function(arg_types, llvm_type(type.return_type)) end def proc_type(type : ProcInstanceType) arg_types = type.arg_types.map { |arg_type| llvm_type(arg_type).as(LLVM::Type) } - LLVM::Type.function(arg_types, llvm_type(type.return_type)).pointer + LLVM::Type.function(arg_types, llvm_type(type.return_type)) end def closure_context_type(vars, parent_llvm_type, self_type) @@ -507,7 +523,11 @@ module Crystal when .double? @llvm_context.double when .pointer? - copy_type(type.element_type).pointer + {% if LibLLVM::IS_LT_150 %} + copy_type(type.element_type).pointer + {% else %} + @llvm_context.pointer + {% end %} when .array? copy_type(type.element_type).array(type.array_size) when .vector? diff --git a/src/compiler/crystal/codegen/match.cr b/src/compiler/crystal/codegen/match.cr index 1a50bdb8fd0a..109366654f29 100644 --- a/src/compiler/crystal/codegen/match.cr +++ b/src/compiler/crystal/codegen/match.cr @@ -39,7 +39,7 @@ class Crystal::CodeGenVisitor private def match_any_type_id_with_function(type, type_id) match_fun_name = "~match<#{type}>" - func = @main_mod.functions[match_fun_name]? || create_match_fun(match_fun_name, type) + func = typed_fun?(@main_mod, match_fun_name) || create_match_fun(match_fun_name, type) func = check_main_fun match_fun_name, func call func, [type_id] of LLVM::Value end diff --git a/src/compiler/crystal/codegen/once.cr b/src/compiler/crystal/codegen/once.cr index 90f18ed48b22..2e91267c1f52 100644 --- a/src/compiler/crystal/codegen/once.cr +++ b/src/compiler/crystal/codegen/once.cr @@ -4,32 +4,34 @@ class Crystal::CodeGenVisitor ONCE_STATE = "~ONCE_STATE" def once_init - if once_init_fun = @main_mod.functions[ONCE_INIT]? + if once_init_fun = typed_fun?(@main_mod, ONCE_INIT) once_init_fun = check_main_fun ONCE_INIT, once_init_fun - once_state_global = @main_mod.globals.add(once_init_fun.return_type, ONCE_STATE) + once_state_global = @main_mod.globals.add(once_init_fun.type.return_type, ONCE_STATE) once_state_global.linkage = LLVM::Linkage::Internal if @single_module - once_state_global.initializer = once_init_fun.return_type.null + once_state_global.initializer = once_init_fun.type.return_type.null state = call once_init_fun store state, once_state_global end end - def run_once(flag, func) + def run_once(flag, func : LLVMTypedFunction) once_fun = main_fun(ONCE) + once_init_fun = main_fun(ONCE_INIT) + + # both of these should be Void* + once_state_type = once_init_fun.type.return_type + once_initializer_type = once_fun.func.params.last.type once_state_global = @llvm_mod.globals[ONCE_STATE]? || begin - once_init_fun = main_fun(ONCE_INIT) - global = @llvm_mod.globals.add(once_init_fun.return_type, ONCE_STATE) + global = @llvm_mod.globals.add(once_state_type, ONCE_STATE) global.linkage = LLVM::Linkage::External global end - call main_fun(ONCE), [ - load(once_state_global), - flag, - bit_cast(func.to_value, once_fun.params.last.type), - ] + state = load(once_state_type, once_state_global) + initializer = pointer_cast(func.func.to_value, once_initializer_type) + call once_fun, [state, flag, initializer] end end diff --git a/src/compiler/crystal/codegen/primitives.cr b/src/compiler/crystal/codegen/primitives.cr index da368301015a..23f4f6c2a58a 100644 --- a/src/compiler/crystal/codegen/primitives.cr +++ b/src/compiler/crystal/codegen/primitives.cr @@ -74,7 +74,7 @@ class Crystal::CodeGenVisitor when "store_atomic" codegen_primitive_store_atomic call, node, target_def, call_args when "throw_info" - cast_to void_ptr_throwinfo, @program.pointer_of(@program.void) + cast_to_void_pointer void_ptr_throwinfo when "va_arg" codegen_va_arg call, node, target_def, call_args else @@ -180,7 +180,7 @@ class Crystal::CodeGenVisitor end llvm_fun = binary_overflow_fun "llvm.#{llvm_op}.with.overflow.i#{calc_width}", calc_type - res_with_overflow = builder.call(llvm_fun, [e1, e2]) + res_with_overflow = builder.call(llvm_fun.type, llvm_fun.func, [e1, e2]) result = extract_value res_with_overflow, 0 overflow = extract_value res_with_overflow, 1 @@ -356,7 +356,7 @@ class Crystal::CodeGenVisitor op_overflow = new_block "overflow" op_normal = new_block "normal" - overflow_condition = builder.call(llvm_expect_i1_fun, [overflow_condition, llvm_false]) + overflow_condition = builder.call(llvm_expect_i1_fun.type, llvm_expect_i1_fun.func, [overflow_condition, llvm_false]) cond overflow_condition, op_overflow, op_normal position_at_end op_overflow @@ -366,15 +366,18 @@ class Crystal::CodeGenVisitor end private def binary_overflow_fun(fun_name, llvm_operand_type) - llvm_mod.functions[fun_name]? || - llvm_mod.functions.add(fun_name, [llvm_operand_type, llvm_operand_type], - llvm_context.struct([llvm_operand_type, llvm_context.int1])) + fetch_typed_fun(@llvm_mod, fun_name) do + LLVM::Type.function( + [llvm_operand_type, llvm_operand_type], + @llvm_context.struct([llvm_operand_type, @llvm_context.int1]), + ) + end end private def llvm_expect_i1_fun - llvm_mod.functions["llvm.expect.i1"]? || - llvm_mod.functions.add("llvm.expect.i1", [llvm_context.int1, llvm_context.int1], - llvm_context.int1) + fetch_typed_fun(@llvm_mod, "llvm.expect.i1") do + LLVM::Type.function([@llvm_context.int1, @llvm_context.int1], @llvm_context.int1) + end end # The below methods (lt, lte, gt, gte, eq, ne) perform @@ -730,11 +733,6 @@ class Crystal::CodeGenVisitor allocate_aggregate base_type - unless type.struct? - type_id_ptr = aggregate_index(@last, 0) - store type_id(base_type), type_id_ptr - end - if type.is_a?(VirtualType) @last = upcast(@last, type, base_type) end @@ -798,19 +796,22 @@ class Crystal::CodeGenVisitor end def codegen_primitive_pointer_add(node, target_def, call_args) - gep call_args[0], call_args[1] + type = context.type.as(PointerInstanceType) + + # `llvm_embedded_type` needed to treat `Void*` like `UInt8*` + gep llvm_embedded_type(type.element_type), call_args[0], call_args[1] end def struct_field_ptr(type, field_name, pointer) index = type.index_of_instance_var('@' + field_name).not_nil! - aggregate_index pointer, index + aggregate_index llvm_type(type), pointer, index end def codegen_primitive_struct_or_union_set(node, target_def, call_args) set_aggregate_field(node, target_def, call_args) do |field_type| type = context.type.as(NonGenericClassType) if type.extern_union? - union_field_ptr(field_type, call_args[0]) + union_field_ptr(type, field_type, call_args[0]) else name = target_def.name.rchop struct_field_ptr(type, name, call_args[0]) @@ -848,10 +849,10 @@ class Crystal::CodeGenVisitor original_call_arg end - def union_field_ptr(field_type, pointer) - ptr = aggregate_index pointer, 0 + def union_field_ptr(union_type, field_type, pointer) + ptr = aggregate_index llvm_type(union_type), pointer, 0 if field_type.is_a?(ProcInstanceType) - bit_cast ptr, @llvm_typer.proc_type(field_type).pointer + pointer_cast ptr, @llvm_typer.proc_type(field_type).pointer.pointer else cast_to_pointer ptr, field_type end @@ -862,11 +863,7 @@ class Crystal::CodeGenVisitor name = external.real_name var = declare_lib_var name, node.type, external.thread_local? - @last = call_args[0] - - if external.type.passed_by_value? - @last = load @last - end + @last = extern_to_rhs(call_args[0], external.type) store @last, var @@ -879,11 +876,7 @@ class Crystal::CodeGenVisitor external = target_def.as(External) var = get_external_var(external) - if external.type.passed_by_value? - @last = var - else - @last = load var - end + @last = extern_to_lhs(var, external.type) @last = check_c_fun node.type, @last @@ -912,7 +905,10 @@ class Crystal::CodeGenVisitor end def codegen_primitive_symbol_to_s(node, target_def, call_args) - load(gep @llvm_mod.globals[SYMBOL_TABLE_NAME], int(0), call_args[0]) + string = llvm_type(@program.string) + table_type = string.array(@symbol_table_values.size) + string_ptr = gep table_type, @llvm_mod.globals[SYMBOL_TABLE_NAME], int(0), call_args[0] + load(string, string_ptr) end def codegen_primitive_class(node, target_def, call_args) @@ -927,7 +923,7 @@ class Crystal::CodeGenVisitor def codegen_primitive_class_with_type(type : VirtualType, value) type_id = type_id(value, type) metaclass_fun_name = "~metaclass" - func = @main_mod.functions[metaclass_fun_name]? || create_metaclass_fun(metaclass_fun_name) + func = typed_fun?(@main_mod, metaclass_fun_name) || create_metaclass_fun(metaclass_fun_name) func = check_main_fun metaclass_fun_name, func call func, [type_id] of LLVM::Value end @@ -981,15 +977,10 @@ class Crystal::CodeGenVisitor proc_type = context.type.as(ProcInstanceType) target_def.args.size.times do |i| - arg = args[i] proc_arg_type = proc_type.arg_types[i] target_def_arg_type = target_def.args[i].type - args[i] = upcast arg, proc_arg_type, target_def_arg_type - if proc_arg_type.passed_by_value? - closure_args << load(args[i]) - else - closure_args << args[i] - end + args[i] = arg = upcast args[i], proc_arg_type, target_def_arg_type + closure_args << to_rhs(arg, proc_arg_type) end fun_ptr = builder.extract_value closure_ptr, 0 @@ -1003,7 +994,8 @@ class Crystal::CodeGenVisitor Phi.open(self, node, @needs_value) do |phi| position_at_end ctx_is_null_block - real_fun_ptr = bit_cast fun_ptr, llvm_proc_type(context.type) + real_fun_llvm_type = llvm_proc_type(context.type) + real_fun_ptr = pointer_cast fun_ptr, real_fun_llvm_type.pointer # When invoking a Proc that has extern structs as arguments or return type, it's tricky: # closures are never generated with C ABI because C doesn't support closures. @@ -1015,13 +1007,13 @@ class Crystal::CodeGenVisitor old_c_calling_convention = target_def.c_calling_convention if c_calling_convention - null_fun_ptr, null_args = codegen_extern_primitive_proc_call(target_def, args, fun_ptr) + null_fun_ptr, null_fun_llvm_type, null_args = codegen_extern_primitive_proc_call(target_def, args, fun_ptr) else - null_fun_ptr, null_args = real_fun_ptr, closure_args + null_fun_ptr, null_fun_llvm_type, null_args = real_fun_ptr, real_fun_llvm_type, closure_args end - null_fun_ptr = LLVM::Function.from_value(null_fun_ptr) + null_fun = LLVMTypedFunction.new(null_fun_llvm_type, LLVM::Function.from_value(null_fun_ptr)) - value = codegen_call_or_invoke(node, target_def, nil, null_fun_ptr, null_args, true, target_def.type, false, proc_type) + value = codegen_call_or_invoke(node, target_def, nil, null_fun, null_args, true, target_def.type, false, proc_type) phi.add value, node.type # Reset abi_info + c_calling_convention so the closure part is generated as usual @@ -1029,10 +1021,11 @@ class Crystal::CodeGenVisitor target_def.c_calling_convention = nil position_at_end ctx_is_not_null_block - real_fun_ptr = bit_cast fun_ptr, llvm_closure_type(context.type) - real_fun_ptr = LLVM::Function.from_value(real_fun_ptr) + real_fun_llvm_type = llvm_closure_type(context.type) + real_fun_ptr = pointer_cast fun_ptr, real_fun_llvm_type.pointer + real_fun = LLVMTypedFunction.new(real_fun_llvm_type, LLVM::Function.from_value(real_fun_ptr)) closure_args.insert(0, ctx_ptr) - value = codegen_call_or_invoke(node, target_def, nil, real_fun_ptr, closure_args, true, target_def.type, true, proc_type) + value = codegen_call_or_invoke(node, target_def, nil, real_fun, closure_args, true, target_def.type, true, proc_type) phi.add value, node.type, true target_def.abi_info = old_abi_info @@ -1065,7 +1058,7 @@ class Crystal::CodeGenVisitor abi_arg_type = abi_info.arg_types[index] case abi_arg_type.kind in .direct? - call_arg = codegen_direct_abi_call(call_arg, abi_arg_type) + call_arg = codegen_direct_abi_call(arg.type, call_arg, abi_arg_type) if cast = abi_arg_type.cast null_fun_types << cast else @@ -1082,17 +1075,20 @@ class Crystal::CodeGenVisitor end null_fun_llvm_type = LLVM::Type.function(null_fun_types, null_fun_return_type) - null_fun_ptr = bit_cast fun_ptr, null_fun_llvm_type.pointer + null_fun_ptr = pointer_cast fun_ptr, null_fun_llvm_type.pointer target_def.c_calling_convention = true - {null_fun_ptr, null_args} + {null_fun_ptr, null_fun_llvm_type, null_args} end def codegen_primitive_pointer_diff(node, target_def, call_args) + type = context.type.as(PointerInstanceType) p0 = ptr2int(call_args[0], llvm_context.int64) p1 = ptr2int(call_args[1], llvm_context.int64) sub = builder.sub p0, p1 - builder.exact_sdiv sub, ptr2int(gep(call_args[0].type.null_pointer, 1), llvm_context.int64) + # `llvm_embedded_type` needed to treat `Void*` like `UInt8*` + offsetted = gep(llvm_embedded_type(type.element_type), call_args[0].type.null_pointer, 1) + builder.exact_sdiv sub, ptr2int(offsetted, llvm_context.int64) end def codegen_primitive_tuple_indexer_known_index(node, target_def, call_args) @@ -1103,9 +1099,10 @@ class Crystal::CodeGenVisitor def codegen_tuple_indexer(type, value, index : Range) case type when TupleInstanceType + struct_type = llvm_type(type) tuple_types = type.tuple_types[index].map &.as(Type) allocate_tuple(@program.tuple_of(tuple_types).as(TupleInstanceType)) do |tuple_type, i| - ptr = aggregate_index value, index.begin + i + ptr = aggregate_index struct_type, value, index.begin + i tuple_value = to_lhs ptr, tuple_type {tuple_type, tuple_value} end @@ -1123,10 +1120,10 @@ class Crystal::CodeGenVisitor def codegen_tuple_indexer(type, value, index : Int32) case type when TupleInstanceType - ptr = aggregate_index value, index + ptr = aggregate_index llvm_type(type), value, index to_lhs ptr, type.tuple_types[index] when NamedTupleInstanceType - ptr = aggregate_index value, index + ptr = aggregate_index llvm_type(type), value, index to_lhs ptr, type.entries[index].type else type = type.instance_type @@ -1143,7 +1140,7 @@ class Crystal::CodeGenVisitor def check_c_fun(type, value) if type.proc? - make_fun(type, bit_cast(value, llvm_context.void_pointer), llvm_context.void_pointer.null) + make_fun(type, cast_to_void_pointer(value), llvm_context.void_pointer.null) else value end @@ -1157,9 +1154,11 @@ class Crystal::CodeGenVisitor failure_ordering = atomic_ordering_get_const(call.args[-1], failure_ordering) value = builder.cmpxchg(ptr, cmp, new, success_ordering, failure_ordering) - value_ptr = alloca llvm_type(node.type) - store extract_value(value, 0), gep(value_ptr, 0, 0) - store extract_value(value, 1), gep(value_ptr, 0, 1) + value_type = node.type.as(TupleInstanceType) + struct_type = llvm_type(value_type) + value_ptr = alloca struct_type + store extract_value(value, 0), gep(struct_type, value_ptr, 0, 0) + store extract_value(value, 1), gep(struct_type, value_ptr, 0, 1) value_ptr end @@ -1192,7 +1191,7 @@ class Crystal::CodeGenVisitor ordering = atomic_ordering_get_const(call.args[-2], ordering) volatile = bool_from_bool_literal(call.args[-1]) - inst = builder.load(ptr) + inst = builder.load(llvm_type(node.type), ptr) inst.ordering = ordering inst.volatile = true if volatile set_alignment inst, node.type diff --git a/src/compiler/crystal/codegen/type_id.cr b/src/compiler/crystal/codegen/type_id.cr index 2bd562784521..5b447d209943 100644 --- a/src/compiler/crystal/codegen/type_id.cr +++ b/src/compiler/crystal/codegen/type_id.cr @@ -14,11 +14,11 @@ class Crystal::CodeGenVisitor end private def type_id_impl(value, type : ReferenceUnionType) - load(value) + load(llvm_context.int32, value) end private def type_id_impl(value, type : VirtualType) - load(value) + load(llvm_context.int32, value) end private def type_id_impl(value, type : NilableReferenceUnionType) @@ -32,7 +32,7 @@ class Crystal::CodeGenVisitor br exit_block position_at_end not_nil_block - phi_table.add insert_block, load(value) + phi_table.add insert_block, load(llvm_context.int32, value) br exit_block position_at_end exit_block diff --git a/src/compiler/crystal/codegen/unions.cr b/src/compiler/crystal/codegen/unions.cr index 628f5e549d2c..1f29763d504a 100644 --- a/src/compiler/crystal/codegen/unions.cr +++ b/src/compiler/crystal/codegen/unions.cr @@ -58,48 +58,55 @@ module Crystal end def union_type_and_value_pointer(union_pointer, type : MixedUnionType) - {load(union_type_id(union_pointer)), union_value(union_pointer)} + struct_type = llvm_type(type) + { + load(llvm_context.int32, union_type_id(struct_type, union_pointer)), + union_value(struct_type, union_pointer), + } end - def union_type_id(union_pointer) - aggregate_index union_pointer, 0 + def union_type_id(struct_type, union_pointer) + aggregate_index struct_type, union_pointer, 0 end - def union_value(union_pointer) - aggregate_index union_pointer, 1 + def union_value(struct_type, union_pointer) + aggregate_index struct_type, union_pointer, 1 end def store_in_union(union_type, union_pointer, value_type, value) - store type_id(value, value_type), union_type_id(union_pointer) - casted_value_ptr = cast_to_pointer(union_value(union_pointer), value_type) + struct_type = llvm_type(union_type) + store type_id(value, value_type), union_type_id(struct_type, union_pointer) + casted_value_ptr = cast_to_pointer(union_value(struct_type, union_pointer), value_type) store value, casted_value_ptr end - def store_bool_in_union(union_type, union_pointer, value) - store type_id(value, @program.bool), union_type_id(union_pointer) + def store_bool_in_union(target_type, union_pointer, value) + struct_type = llvm_type(target_type) + store type_id(value, @program.bool), union_type_id(struct_type, union_pointer) # To store a boolean in a union # we sign-extend it to the size in bits of the union - union_value_type = @llvm_typer.union_value_type(union_type) - union_size = @llvm_typer.size_of(union_value_type) + union_size = @llvm_typer.size_of(struct_type.struct_element_types[1]) int_type = llvm_context.int((union_size * 8).to_i32) bool_as_extended_int = builder.zext(value, int_type) - casted_value_ptr = bit_cast(union_value(union_pointer), int_type.pointer) + casted_value_ptr = pointer_cast(union_value(struct_type, union_pointer), int_type.pointer) store bool_as_extended_int, casted_value_ptr end - def store_nil_in_union(union_pointer, target_type) - union_value_type = @llvm_typer.union_value_type(target_type) + def store_nil_in_union(target_type, union_pointer) + struct_type = llvm_type(target_type) + union_value_type = struct_type.struct_element_types[1] value = union_value_type.null - store type_id(value, @program.nil), union_type_id(union_pointer) - casted_value_ptr = bit_cast union_value(union_pointer), union_value_type.pointer + store type_id(value, @program.nil), union_type_id(struct_type, union_pointer) + casted_value_ptr = pointer_cast union_value(struct_type, union_pointer), union_value_type.pointer store value, casted_value_ptr end - def store_void_in_union(union_pointer, target_type) - store type_id(@program.void), union_type_id(union_pointer) + def store_void_in_union(target_type, union_pointer) + struct_type = llvm_type(target_type) + store type_id(@program.void), union_type_id(struct_type, union_pointer) end def assign_distinct_union_types(target_pointer, target_type, value_type, value) @@ -114,7 +121,7 @@ module Crystal # - cast the target pointer to Pointer(A | B) # - store the A | B from the first pointer into the casted target pointer casted_target_pointer = cast_to_pointer target_pointer, value_type - store load(value), casted_target_pointer + store load(llvm_type(value_type), value), casted_target_pointer end def downcast_distinct_union_types(value, to_type : MixedUnionType, from_type : MixedUnionType) diff --git a/src/intrinsics.cr b/src/intrinsics.cr index f9c4302f7d0d..9b87e2c326ea 100644 --- a/src/intrinsics.cr +++ b/src/intrinsics.cr @@ -5,23 +5,45 @@ lib LibIntrinsics fun debugtrap = "llvm.debugtrap" {% if flag?(:bits64) %} - {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memcpy)] {% end %} - fun memcpy = "llvm.memcpy.p0i8.p0i8.i64"(dest : Void*, src : Void*, len : UInt64, is_volatile : Bool) + {% if compare_versions(Crystal::LLVM_VERSION, "15.0.0") < 0 %} + {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memcpy)] {% end %} + fun memcpy = "llvm.memcpy.p0i8.p0i8.i64"(dest : Void*, src : Void*, len : UInt64, is_volatile : Bool) - {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memmove)] {% end %} - fun memmove = "llvm.memmove.p0i8.p0i8.i64"(dest : Void*, src : Void*, len : UInt64, is_volatile : Bool) + {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memmove)] {% end %} + fun memmove = "llvm.memmove.p0i8.p0i8.i64"(dest : Void*, src : Void*, len : UInt64, is_volatile : Bool) - {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memset)] {% end %} - fun memset = "llvm.memset.p0i8.i64"(dest : Void*, val : UInt8, len : UInt64, is_volatile : Bool) + {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memset)] {% end %} + fun memset = "llvm.memset.p0i8.i64"(dest : Void*, val : UInt8, len : UInt64, is_volatile : Bool) + {% else %} + {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memcpy)] {% end %} + fun memcpy = "llvm.memcpy.p0.p0.i64"(dest : Void*, src : Void*, len : UInt64, is_volatile : Bool) + + {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memmove)] {% end %} + fun memmove = "llvm.memmove.p0.p0.i64"(dest : Void*, src : Void*, len : UInt64, is_volatile : Bool) + + {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memset)] {% end %} + fun memset = "llvm.memset.p0.i64"(dest : Void*, val : UInt8, len : UInt64, is_volatile : Bool) + {% end %} {% else %} - {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memcpy)] {% end %} - fun memcpy = "llvm.memcpy.p0i8.p0i8.i32"(dest : Void*, src : Void*, len : UInt32, is_volatile : Bool) + {% if compare_versions(Crystal::LLVM_VERSION, "15.0.0") < 0 %} + {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memcpy)] {% end %} + fun memcpy = "llvm.memcpy.p0i8.p0i8.i32"(dest : Void*, src : Void*, len : UInt32, is_volatile : Bool) - {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memmove)] {% end %} - fun memmove = "llvm.memmove.p0i8.p0i8.i32"(dest : Void*, src : Void*, len : UInt32, is_volatile : Bool) + {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memmove)] {% end %} + fun memmove = "llvm.memmove.p0i8.p0i8.i32"(dest : Void*, src : Void*, len : UInt32, is_volatile : Bool) - {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memset)] {% end %} - fun memset = "llvm.memset.p0i8.i32"(dest : Void*, val : UInt8, len : UInt32, is_volatile : Bool) + {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memset)] {% end %} + fun memset = "llvm.memset.p0i8.i32"(dest : Void*, val : UInt8, len : UInt32, is_volatile : Bool) + {% else %} + {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memcpy)] {% end %} + fun memcpy = "llvm.memcpy.p0.p0.i32"(dest : Void*, src : Void*, len : UInt32, is_volatile : Bool) + + {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memmove)] {% end %} + fun memmove = "llvm.memmove.p0.p0.i32"(dest : Void*, src : Void*, len : UInt32, is_volatile : Bool) + + {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memset)] {% end %} + fun memset = "llvm.memset.p0.i32"(dest : Void*, val : UInt8, len : UInt32, is_volatile : Bool) + {% end %} {% end %} {% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_read_cycle_counter)] {% end %} diff --git a/src/llvm/builder.cr b/src/llvm/builder.cr index ab743655619f..2b254f991d60 100644 --- a/src/llvm/builder.cr +++ b/src/llvm/builder.cr @@ -49,13 +49,22 @@ class LLVM::Builder Value.new phi_node end - def call(func, name : String = "") + @[Deprecated("Pass the function type of `func` as well (equal to `func.function_type`) in order to support LLVM 15+")] + def call(func : LLVM::Function, name : String = "") # check_func(func) Value.new LibLLVM.build_call2(self, func.function_type, func, nil, 0, name) end - def call(func, arg : LLVM::Value, name : String = "") + def call(type : LLVM::Type, func : LLVM::Function, name : String = "") + # check_type("call", type) + # check_func(func) + + Value.new LibLLVM.build_call2(self, type, func, nil, 0, name) + end + + @[Deprecated("Pass the function type of `func` as well (equal to `func.function_type`) in order to support LLVM 15+")] + def call(func : LLVM::Function, arg : LLVM::Value, name : String = "") # check_func(func) # check_value(arg) @@ -63,13 +72,31 @@ class LLVM::Builder Value.new LibLLVM.build_call2(self, func.function_type, func, pointerof(value), 1, name) end - def call(func, args : Array(LLVM::Value), name : String = "", bundle : LLVM::OperandBundleDef = LLVM::OperandBundleDef.null) + def call(type : LLVM::Type, func : LLVM::Function, arg : LLVM::Value, name : String = "") + # check_type("call", type) + # check_func(func) + # check_value(arg) + + value = arg.to_unsafe + Value.new LibLLVM.build_call2(self, type, func, pointerof(value), 1, name) + end + + @[Deprecated("Pass the function type of `func` as well (equal to `func.function_type`) in order to support LLVM 15+")] + def call(func : LLVM::Function, args : Array(LLVM::Value), name : String = "", bundle : LLVM::OperandBundleDef = LLVM::OperandBundleDef.null) # check_func(func) # check_values(args) Value.new LibLLVMExt.build_call2(self, func.function_type, func, (args.to_unsafe.as(LibLLVM::ValueRef*)), args.size, bundle, name) end + def call(type : LLVM::Type, func : LLVM::Function, args : Array(LLVM::Value), name : String = "", bundle : LLVM::OperandBundleDef = LLVM::OperandBundleDef.null) + # check_type("call", type) + # check_func(func) + # check_values(args) + + Value.new LibLLVMExt.build_call2(self, type, func, (args.to_unsafe.as(LibLLVM::ValueRef*)), args.size, bundle, name) + end + def alloca(type, name = "") # check_type("alloca", type) @@ -83,41 +110,81 @@ class LLVM::Builder Value.new LibLLVM.build_store(self, value, ptr) end - def load(ptr, name = "") + @[Deprecated("Pass the pointee of `ptr` as well (equal to `ptr.type.element_type`) in order to support LLVM 15+")] + def load(ptr : LLVM::Value, name = "") + # check_value(ptr) + + Value.new LibLLVM.build_load2(self, ptr.type.element_type, ptr, name) + end + + def load(type : LLVM::Type, ptr : LLVM::Value, name = "") + # check_type("load", type) # check_value(ptr) - Value.new LibLLVM.build_load(self, ptr, name) + Value.new LibLLVM.build_load2(self, type, ptr, name) end def store_volatile(value, ptr) store(value, ptr).tap { |v| v.volatile = true } end - def load_volatile(ptr, name = "") + def load_volatile(ptr : LLVM::Value, name = "") load(ptr, name).tap { |v| v.volatile = true } end + def load_volatile(type : LLVM::Type, ptr : LLVM::Value, name = "") + load(type, ptr, name).tap { |v| v.volatile = true } + end + {% for method_name in %w(gep inbounds_gep) %} - def {{method_name.id}}(value, indices : Array(LLVM::ValueRef), name = "") + @[Deprecated("Pass the type of `value` as well (equal to `value.type`) in order to support LLVM 15+")] + def {{method_name.id}}(value : LLVM::Value, indices : Array(LLVM::ValueRef), name = "") # check_value(value) - Value.new LibLLVM.build_{{method_name.id}}(self, value, indices.to_unsafe.as(LibLLVM::ValueRef*), indices.size, name) + Value.new LibLLVM.build_{{method_name.id}}2(self, value.type, value, indices.to_unsafe.as(LibLLVM::ValueRef*), indices.size, name) end - def {{method_name.id}}(value, index : LLVM::Value, name = "") + def {{method_name.id}}(type : LLVM::Type, value : LLVM::Value, indices : Array(LLVM::ValueRef), name = "") + # check_type({{method_name}}, type) + # check_value(value) + + Value.new LibLLVM.build_{{method_name.id}}2(self, type, value, indices.to_unsafe.as(LibLLVM::ValueRef*), indices.size, name) + end + + @[Deprecated("Pass the type of `value` as well (equal to `value.type`) in order to support LLVM 15+")] + def {{method_name.id}}(value : LLVM::Value, index : LLVM::Value, name = "") + # check_value(value) + + indices = pointerof(index).as(LibLLVM::ValueRef*) + Value.new LibLLVM.build_{{method_name.id}}2(self, value.type, value, indices, 1, name) + end + + def {{method_name.id}}(type : LLVM::Type, value : LLVM::Value, index : LLVM::Value, name = "") + # check_type({{method_name}}, type) # check_value(value) indices = pointerof(index).as(LibLLVM::ValueRef*) - Value.new LibLLVM.build_{{method_name.id}}(self, value, indices, 1, name) + Value.new LibLLVM.build_{{method_name.id}}2(self, type, value, indices, 1, name) + end + + @[Deprecated("Pass the type of `value` as well (equal to `value.type`) in order to support LLVM 15+")] + def {{method_name.id}}(value : LLVM::Value, index1 : LLVM::Value, index2 : LLVM::Value, name = "") + # check_value(value) + + indices = uninitialized LLVM::Value[2] + indices[0] = index1 + indices[1] = index2 + Value.new LibLLVM.build_{{method_name.id}}2(self, value.type, value, indices.to_unsafe.as(LibLLVM::ValueRef*), 2, name) end - def {{method_name.id}}(value, index1 : LLVM::Value, index2 : LLVM::Value, name = "") + def {{method_name.id}}(type : LLVM::Type, value : LLVM::Value, index1 : LLVM::Value, index2 : LLVM::Value, name = "") + # check_type({{method_name}}, type) # check_value(value) indices = uninitialized LLVM::Value[2] indices[0] = index1 indices[1] = index2 - Value.new LibLLVM.build_{{method_name.id}}(self, value, indices.to_unsafe.as(LibLLVM::ValueRef*), 2, name) + Value.new LibLLVM.build_{{method_name.id}}2(self, type, value, indices.to_unsafe.as(LibLLVM::ValueRef*), 2, name) end {% end %} @@ -207,12 +274,20 @@ class LLVM::Builder LibLLVMExt.build_catch_ret(self, pad, basic_block) end + @[Deprecated("Pass the function type of `fn` as well (equal to `fn.function_type`) in order to support LLVM 15+")] def invoke(fn : LLVM::Function, args : Array(LLVM::Value), a_then, a_catch, bundle : LLVM::OperandBundleDef = LLVM::OperandBundleDef.null, name = "") # check_func(fn) Value.new LibLLVMExt.build_invoke2 self, fn.function_type, fn, (args.to_unsafe.as(LibLLVM::ValueRef*)), args.size, a_then, a_catch, bundle, name end + def invoke(type : LLVM::Type, fn : LLVM::Function, args : Array(LLVM::Value), a_then, a_catch, bundle : LLVM::OperandBundleDef = LLVM::OperandBundleDef.null, name = "") + # check_type("invoke", type) + # check_func(fn) + + Value.new LibLLVMExt.build_invoke2 self, type, fn, (args.to_unsafe.as(LibLLVM::ValueRef*)), args.size, a_then, a_catch, bundle, name + end + def switch(value, otherwise, cases) # check_value(value) diff --git a/src/llvm/context.cr b/src/llvm/context.cr index a1c3ec87adfc..f226edcafcba 100644 --- a/src/llvm/context.cr +++ b/src/llvm/context.cr @@ -59,8 +59,20 @@ class LLVM::Context Type.new LibLLVM.double_type_in_context(self) end + def pointer : Type + {% if LibLLVM::IS_LT_150 %} + {% raise "Opaque pointers are only supported on LLVM 15.0 or above" %} + {% else %} + Type.new LibLLVM.pointer_type_in_context(self, 0) + {% end %} + end + def void_pointer : Type - int8.pointer + {% if LibLLVM::IS_LT_150 %} + int8.pointer + {% else %} + pointer + {% end %} end def struct(name : String, packed = false, &) : Type diff --git a/src/llvm/enums.cr b/src/llvm/enums.cr index 29b0ddedd8fa..7b9d4d260e07 100644 --- a/src/llvm/enums.cr +++ b/src/llvm/enums.cr @@ -428,6 +428,13 @@ module LLVM PreserveAccessIndex = 27 # "llvm.preserve.*.access.index" end end + + enum UWTableKind + None = 0 # No unwind table requested + Sync = 1 # "Synchronous" unwind tables + Async = 2 # "Asynchronous" unwind tables (instr precise) + Default = 2 + end end require "./enums/*" diff --git a/src/llvm/ext/llvm-versions.txt b/src/llvm/ext/llvm-versions.txt index d8dbd4849ebe..3895e35b2bcc 100644 --- a/src/llvm/ext/llvm-versions.txt +++ b/src/llvm/ext/llvm-versions.txt @@ -1 +1 @@ -14.0 13.0 12.0 11.1 11.0 10.0 9.0 8.0 +15.0 14.0 13.0 12.0 11.1 11.0 10.0 9.0 8.0 diff --git a/src/llvm/function.cr b/src/llvm/function.cr index 20c2a4ebbad2..1b4d803fc08f 100644 --- a/src/llvm/function.cr +++ b/src/llvm/function.cr @@ -33,6 +33,16 @@ struct LLVM::Function end end + def add_attribute(attribute : Attribute, index = AttributeIndex::FunctionIndex, *, value) + return if attribute.value == 0 + + context = LibLLVM.get_module_context(LibLLVM.get_global_parent(self)) + attribute.each_kind do |kind| + attribute_ref = LibLLVM.create_enum_attribute(context, kind, value.to_u64) + LibLLVM.add_attribute_at_index(self, index, attribute_ref) + end + end + def add_target_dependent_attribute(name, value) LibLLVM.add_target_dependent_function_attr self, name, value end @@ -47,16 +57,19 @@ struct LLVM::Function attrs end + @[Deprecated] def function_type Type.new LibLLVM.get_element_type(LibLLVM.type_of(self)) end + @[Deprecated] def return_type - function_type.return_type + Type.new(LibLLVM.get_element_type(LibLLVM.type_of(self))).return_type end + @[Deprecated] def varargs? - function_type.varargs? + Type.new(LibLLVM.get_element_type(LibLLVM.type_of(self))).varargs? end def params diff --git a/src/llvm/function_collection.cr b/src/llvm/function_collection.cr index a7fcc7cbfcd7..62e2bd2e6fc2 100644 --- a/src/llvm/function_collection.cr +++ b/src/llvm/function_collection.cr @@ -4,14 +4,22 @@ struct LLVM::FunctionCollection def add(name, arg_types : Array(LLVM::Type), ret_type, varargs = false) # check_types_context(name, arg_types, ret_type) + add(name, LLVM::Type.function(arg_types, ret_type, varargs)) + end - fun_type = LLVM::Type.function(arg_types, ret_type, varargs) + def add(name, arg_types : Array(LLVM::Type), ret_type, varargs = false, &) + func = add(name, arg_types, ret_type, varargs) + yield func + func + end + + def add(name, fun_type : LLVM::Type) func = LibLLVM.add_function(@mod, name, fun_type) Function.new(func) end - def add(name, arg_types : Array(LLVM::Type), ret_type, varargs = false, &) - func = add(name, arg_types, ret_type, varargs) + def add(name, fun_type : LLVM::Type, &) + func = add(name, fun_type) yield func func end diff --git a/src/llvm/lib_llvm.cr b/src/llvm/lib_llvm.cr index 7c6156703162..0393410724f8 100644 --- a/src/llvm/lib_llvm.cr +++ b/src/llvm/lib_llvm.cr @@ -19,6 +19,7 @@ end {% begin %} lib LibLLVM + IS_150 = {{LibLLVM::VERSION.starts_with?("15.0")}} IS_140 = {{LibLLVM::VERSION.starts_with?("14.0")}} IS_130 = {{LibLLVM::VERSION.starts_with?("13.0")}} IS_120 = {{LibLLVM::VERSION.starts_with?("12.0")}} @@ -33,6 +34,8 @@ end IS_LT_110 = {{compare_versions(LibLLVM::VERSION, "11.0.0") < 0}} IS_LT_120 = {{compare_versions(LibLLVM::VERSION, "12.0.0") < 0}} IS_LT_130 = {{compare_versions(LibLLVM::VERSION, "13.0.0") < 0}} + IS_LT_140 = {{compare_versions(LibLLVM::VERSION, "14.0.0") < 0}} + IS_LT_150 = {{compare_versions(LibLLVM::VERSION, "15.0.0") < 0}} end {% end %} @@ -95,10 +98,6 @@ lib LibLLVM fun build_atomicrmw = LLVMBuildAtomicRMW(builder : BuilderRef, op : LLVM::AtomicRMWBinOp, ptr : ValueRef, val : ValueRef, ordering : LLVM::AtomicOrdering, singlethread : Int32) : ValueRef fun build_bit_cast = LLVMBuildBitCast(builder : BuilderRef, value : ValueRef, type : TypeRef, name : UInt8*) : ValueRef fun build_br = LLVMBuildBr(builder : BuilderRef, block : BasicBlockRef) : ValueRef - {% if LibLLVM::IS_LT_110 %} - # LLVMBuildCall is deprecated in favor of LLVMBuildCall2, in preparation for opaque pointer types. - fun build_call = LLVMBuildCall(builder : BuilderRef, fn : ValueRef, args : ValueRef*, num_args : Int32, name : UInt8*) : ValueRef - {% end %} fun build_call2 = LLVMBuildCall2(builder : BuilderRef, type : TypeRef, fn : ValueRef, args : ValueRef*, num_args : Int32, name : UInt8*) : ValueRef fun build_cond = LLVMBuildCondBr(builder : BuilderRef, if : ValueRef, then : BasicBlockRef, else : BasicBlockRef) : ValueRef fun build_exact_sdiv = LLVMBuildExactSDiv(builder : BuilderRef, lhs : ValueRef, rhs : ValueRef, name : UInt8*) : ValueRef @@ -113,18 +112,14 @@ lib LibLLVM fun build_fpext = LLVMBuildFPExt(builder : BuilderRef, val : ValueRef, dest_ty : TypeRef, name : UInt8*) : ValueRef fun build_fptrunc = LLVMBuildFPTrunc(builder : BuilderRef, val : ValueRef, dest_ty : TypeRef, name : UInt8*) : ValueRef fun build_fsub = LLVMBuildFSub(builder : BuilderRef, lhs : ValueRef, rhs : ValueRef, name : UInt8*) : ValueRef - fun build_gep = LLVMBuildGEP(builder : BuilderRef, pointer : ValueRef, indices : ValueRef*, num_indices : UInt32, name : UInt8*) : ValueRef - fun build_inbounds_gep = LLVMBuildInBoundsGEP(builder : BuilderRef, pointer : ValueRef, indices : ValueRef*, num_indices : UInt32, name : UInt8*) : ValueRef + fun build_gep2 = LLVMBuildGEP2(builder : BuilderRef, ty : TypeRef, pointer : ValueRef, indices : ValueRef*, num_indices : UInt32, name : UInt8*) : ValueRef + fun build_inbounds_gep2 = LLVMBuildInBoundsGEP2(builder : BuilderRef, ty : TypeRef, pointer : ValueRef, indices : ValueRef*, num_indices : UInt32, name : UInt8*) : ValueRef fun build_global_string_ptr = LLVMBuildGlobalStringPtr(builder : BuilderRef, str : UInt8*, name : UInt8*) : ValueRef fun build_icmp = LLVMBuildICmp(builder : BuilderRef, op : LLVM::IntPredicate, lhs : ValueRef, rhs : ValueRef, name : UInt8*) : ValueRef fun build_int2ptr = LLVMBuildIntToPtr(builder : BuilderRef, val : ValueRef, dest_ty : TypeRef, name : UInt8*) : ValueRef - {% if LibLLVM::IS_LT_110 %} - # LLVMBuildInvoke is deprecated in favor of LLVMBuildInvoke2, in preparation for opaque pointer types. - fun build_invoke = LLVMBuildInvoke(builder : BuilderRef, fn : ValueRef, args : ValueRef*, num_args : UInt32, then : BasicBlockRef, catch : BasicBlockRef, name : UInt8*) : ValueRef - {% end %} fun build_invoke2 = LLVMBuildInvoke2(builder : BuilderRef, ty : TypeRef, fn : ValueRef, args : ValueRef*, num_args : UInt32, then : BasicBlockRef, catch : BasicBlockRef, name : UInt8*) : ValueRef fun build_landing_pad = LLVMBuildLandingPad(builder : BuilderRef, ty : TypeRef, pers_fn : ValueRef, num_clauses : UInt32, name : UInt8*) : ValueRef - fun build_load = LLVMBuildLoad(builder : BuilderRef, ptr : ValueRef, name : UInt8*) : ValueRef + fun build_load2 = LLVMBuildLoad2(builder : BuilderRef, ty : TypeRef, ptr : ValueRef, name : UInt8*) : ValueRef fun build_lshr = LLVMBuildLShr(builder : BuilderRef, lhs : ValueRef, rhs : ValueRef, name : UInt8*) : ValueRef fun build_malloc = LLVMBuildMalloc(builder : BuilderRef, type : TypeRef, name : UInt8*) : ValueRef fun build_mul = LLVMBuildMul(builder : BuilderRef, lhs : ValueRef, rhs : ValueRef, name : UInt8*) : ValueRef @@ -177,7 +172,6 @@ lib LibLLVM fun generic_value_to_pointer = LLVMGenericValueToPointer(value : GenericValueRef) : Void* fun get_basic_block_name = LLVMGetBasicBlockName(basic_block : LibLLVM::BasicBlockRef) : Char* fun get_current_debug_location = LLVMGetCurrentDebugLocation(builder : BuilderRef) : ValueRef - fun get_element_type = LLVMGetElementType(ty : TypeRef) : TypeRef fun get_first_instruction = LLVMGetFirstInstruction(block : BasicBlockRef) : ValueRef fun get_first_target = LLVMGetFirstTarget : TargetRef fun get_first_basic_block = LLVMGetFirstBasicBlock(fn : ValueRef) : BasicBlockRef @@ -365,6 +359,9 @@ lib LibLLVM fun float_type_in_context = LLVMFloatTypeInContext(ContextRef) : TypeRef fun double_type_in_context = LLVMDoubleTypeInContext(ContextRef) : TypeRef fun struct_type_in_context = LLVMStructTypeInContext(c : ContextRef, element_types : TypeRef*, element_count : UInt32, packed : Int32) : TypeRef + {% unless LibLLVM::IS_LT_150 %} + fun pointer_type_in_context = LLVMPointerTypeInContext(ContextRef, address_space : UInt) : TypeRef + {% end %} fun const_string_in_context = LLVMConstStringInContext(c : ContextRef, str : UInt8*, length : UInt32, dont_null_terminate : Int32) : ValueRef fun const_struct_in_context = LLVMConstStructInContext(c : ContextRef, contant_vals : ValueRef*, count : UInt32, packed : Int32) : ValueRef diff --git a/src/llvm/parameter_collection.cr b/src/llvm/parameter_collection.cr index d9e850b987d6..937d9ec2edcc 100644 --- a/src/llvm/parameter_collection.cr +++ b/src/llvm/parameter_collection.cr @@ -5,7 +5,7 @@ struct LLVM::ParameterCollection end def size - @function.function_type.params_size + LibLLVM.get_count_params(@function).to_i end def to_a @@ -21,6 +21,6 @@ struct LLVM::ParameterCollection end def types - @function.function_type.params_types + to_a.map(&.type) end end diff --git a/src/llvm/type.cr b/src/llvm/type.cr index a4731fddc4ba..cd3e5f9d5264 100644 --- a/src/llvm/type.cr +++ b/src/llvm/type.cr @@ -42,7 +42,11 @@ struct LLVM::Type end def pointer : LLVM::Type - Type.new LibLLVM.pointer_type(self, 0) + {% if LibLLVM::IS_LT_150 %} + Type.new LibLLVM.pointer_type(self, 0) + {% else %} + Type.new LibLLVM.pointer_type_in_context(LibLLVM.get_type_context(self), 0) + {% end %} end def array(count) : LLVM::Type @@ -85,8 +89,14 @@ struct LLVM::Type def element_type : LLVM::Type case kind - when Kind::Array, Kind::Vector, Kind::Pointer + when Kind::Array, Kind::Vector Type.new LibLLVM.get_element_type(self) + when Kind::Pointer + {% if LibLLVM::IS_LT_150 %} + Type.new LibLLVM.get_element_type(self) + {% else %} + raise "Typed pointers are unavailable on LLVM 15.0 or above" + {% end %} else raise "Not a sequential type" end