Skip to content

Commit

Permalink
Support LLVM 15 (#13164)
Browse files Browse the repository at this point in the history
* allow using llvm 15

* use opaque pointers on llvm 15+

* opaque pointer names in llvm intrinsics

* uwtable -> uwtable(async)

* use unnamed llvm structs for lib fun bindings of llvm intrinsics

* ci support

* fix uwtable value

* `uwtable(async)` outside aarch64

* don't use string interpolation
  • Loading branch information
HertzDevil authored Mar 9, 2023
1 parent 082b745 commit 0a5a25b
Show file tree
Hide file tree
Showing 15 changed files with 128 additions and 13 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,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-18.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-18.04
runs-on: ${{ matrix.base_image }}
name: "Test LLVM ${{ matrix.llvm_version }} (${{ matrix.base_image }})"
steps:
Expand Down
3 changes: 2 additions & 1 deletion spec/compiler/codegen/primitives_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions spec/llvm-ir/test.sh
Original file line number Diff line number Diff line change
@@ -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")"
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/crystal/codegen/ast.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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}:
Expand Down
14 changes: 12 additions & 2 deletions src/compiler/crystal/codegen/codegen.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2213,7 +2213,12 @@ module Crystal
end

private def c_memset_fun
name = @program.bits64? ? "llvm.memset.p0i8.i64" : "llvm.memset.p0i8.i32"
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]
Expand All @@ -2222,7 +2227,12 @@ module Crystal
end

private def c_memcpy_fun
name = @program.bits64? ? "llvm.memcpy.p0i8.p0i8.i64" : "llvm.memcpy.p0i8.p0i8.i32"
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]
Expand Down
14 changes: 12 additions & 2 deletions src/compiler/crystal/codegen/fun.cr
Original file line number Diff line number Diff line change
Expand Up @@ -95,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
{% if compare_versions(Crystal::LLVM_VERSION, "8.0.0") < 0 %}
Expand Down Expand Up @@ -302,7 +307,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)
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/crystal/codegen/llvm_builder_helper.cr
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,8 @@ module Crystal
end

delegate llvm_type, llvm_struct_type, llvm_arg_type, llvm_embedded_type,
llvm_c_type, llvm_c_return_type, llvm_return_type, llvm_embedded_c_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))
Expand Down
22 changes: 21 additions & 1 deletion src/compiler/crystal/codegen/llvm_typer.cr
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,22 @@ 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)
Expand Down Expand Up @@ -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?
Expand Down
22 changes: 20 additions & 2 deletions src/intrinsics.cr
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ lib LibIntrinsics

{% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memset)] {% end %}
fun memset = "llvm.memset.p0i8.i64"(dest : Void*, val : UInt8, len : UInt64, align : UInt32, is_volatile : Bool)
{% else %}
{% elsif 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)

Expand All @@ -23,6 +23,15 @@ lib LibIntrinsics

{% 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 compare_versions(Crystal::LLVM_VERSION, "7.0.0") < 0 %}
Expand All @@ -34,7 +43,7 @@ lib LibIntrinsics

{% if flag?(:interpreted) %} @[Primitive(:interpreter_intrinsics_memset)] {% end %}
fun memset = "llvm.memset.p0i8.i32"(dest : Void*, val : UInt8, len : UInt32, align : UInt32, is_volatile : Bool)
{% else %}
{% elsif 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)

Expand All @@ -43,6 +52,15 @@ lib LibIntrinsics

{% 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 %}

Expand Down
14 changes: 13 additions & 1 deletion src/llvm/context.cr
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,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
Expand Down
7 changes: 7 additions & 0 deletions src/llvm/enums.cr
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,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/*"
2 changes: 1 addition & 1 deletion src/llvm/ext/llvm-versions.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
14.0 13.0 12.0 11.1 11.0 10.0 9.0 8.0 7.1 6.0 5.0 4.0 3.9 3.8
15.0 14.0 13.0 12.0 11.1 11.0 10.0 9.0 8.0 7.1 6.0 5.0 4.0 3.9 3.8
10 changes: 10 additions & 0 deletions src/llvm/function.cr
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,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
Expand Down
6 changes: 6 additions & 0 deletions src/llvm/lib_llvm.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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")}}
Expand All @@ -42,6 +43,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 %}

Expand Down Expand Up @@ -407,6 +410,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
Expand Down
14 changes: 12 additions & 2 deletions src/llvm/type.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 0a5a25b

Please sign in to comment.