Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support LLVM 15 #13173

Merged
merged 10 commits into from
Mar 11, 2023
3 changes: 3 additions & 0 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
63 changes: 38 additions & 25 deletions samples/llvm/brainfuck.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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"

Expand All @@ -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

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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
Expand Down
2 changes: 0 additions & 2 deletions spec/compiler/codegen/pointer_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
))
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
2 changes: 1 addition & 1 deletion src/compiler/crystal/codegen/asm.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
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
22 changes: 11 additions & 11 deletions src/compiler/crystal/codegen/call.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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?
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down
Loading