Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/intrinsics/Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "SPIRVIntrinsics"
uuid = "71d1d633-e7e8-4a92-83a1-de8814b09ba8"
authors = ["Tim Besard <tim.besard@gmail.com>"]
version = "0.5.9"
version = "0.5.10"

[deps]
ExprTools = "e2ba6199-217a-4e67-a87a-7c52f15ade04"
Expand All @@ -18,7 +18,7 @@ SPIRVIntrinsicsSIMDExt = "SIMD"
[compat]
ExprTools = "0.1"
GPUToolbox = "0.2, 0.3, 1"
LLVM = "9.1"
LLVM = "10"
SIMD = "3.6"
SpecialFunctions = "1.3, 2"
julia = "1.10"
2 changes: 1 addition & 1 deletion lib/intrinsics/src/SPIRVIntrinsics.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module SPIRVIntrinsics

using LLVM, LLVM.Interop
using LLVM, LLVM.IR, LLVM.Build, LLVM.Interop
using Core: LLVMPtr

import ExprTools
Expand Down
50 changes: 17 additions & 33 deletions lib/intrinsics/src/memory.jl
Original file line number Diff line number Diff line change
@@ -1,38 +1,22 @@
# local memory

# get a pointer to local memory, with known (static) or zero length (dynamic)
@generated function emit_localmemory(::Type{T}, ::Val{len}=Val(0)) where {T,len}
Context() do ctx
# XXX: as long as LLVMPtr is emitted as i8*, it doesn't make sense to type the GV
eltyp = convert(LLVMType, LLVM.Int8Type())
T_ptr = convert(LLVMType, LLVMPtr{T,AS.Workgroup})

# create a function
llvm_f, _ = create_function(T_ptr)

# create the global variable
mod = LLVM.parent(llvm_f)
gv_typ = LLVM.ArrayType(eltyp, len * sizeof(T))
gv = GlobalVariable(mod, gv_typ, "local_memory", AS.Workgroup)
if len > 0
linkage!(gv, LLVM.API.LLVMInternalLinkage)
initializer!(gv, null(gv_typ))
end
# TODO: Make the alignment configurable
alignment!(gv, Base.datatype_alignment(T))

# generate IR
IRBuilder() do builder
entry = BasicBlock(llvm_f, "entry")
position!(builder, entry)

ptr = gep!(builder, gv_typ, gv, [ConstantInt(0), ConstantInt(0)])

untyped_ptr = bitcast!(builder, ptr, T_ptr)

ret!(builder, untyped_ptr)
end

call_function(llvm_f, LLVMPtr{T,AS.Workgroup})
@llvmgenerated builder function emit_localmemory(::Type{T},
::Val{len}=Val(0))::LLVMPtr{T,AS.Workgroup} where {T,len}
# XXX: as long as LLVMPtr is emitted as i8*, it doesn't make sense to type the GV
eltyp = LLVM.Int8Type()
T_ptr = convert(LLVMType, LLVMPtr{T,AS.Workgroup})

# create the global variable
gv_typ = LLVM.ArrayType(eltyp, len * sizeof(T))
gv = GlobalVariable(current_module(builder), gv_typ, "local_memory", AS.Workgroup)
if len > 0
gv.linkage = LLVM.Linkage.Internal
gv.initializer = null(gv_typ)
end
# TODO: Make the alignment configurable
gv.alignment = Base.datatype_alignment(T)

ptr = gep!(builder, gv_typ, gv, [ConstantInt(0), ConstantInt(0)])
bitcast!(builder, ptr, T_ptr)
end
114 changes: 51 additions & 63 deletions lib/intrinsics/src/printf.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,73 +26,61 @@ end

@generated function emit_printf(::Val{fmt}, argspec...) where {fmt}
arg_exprs = [:( argspec[$i] ) for i in 1:length(argspec)]
arg_types = [argspec...]

Context() do ctx
T_void = LLVM.VoidType()
T_int32 = LLVM.Int32Type()
T_pint8 = LLVM.PointerType(LLVM.Int8Type(), AS.UniformConstant)

# create functions
param_types = LLVMType[convert(LLVMType, typ) for typ in arg_types]
llvm_f, _ = create_function(T_int32, param_types)
mod = LLVM.parent(llvm_f)

IRBuilder() do builder
entry = BasicBlock(llvm_f, "entry")
position!(builder, entry)

# `printf` needs to be invoked very specifically, e.g., the format string needs
# to be a pointer to a string, and arguments need to match exactly what is
# expected by the format string, so we cannot rely on how the arguments to this
# function have been passed in (by `llvmcall`).
T_actual_args = LLVMType[]
actual_args = LLVM.Value[]
for (_, (arg, argtyp)) in enumerate(zip(parameters(llvm_f), arg_types))
if argtyp <: LLVMPtr
# passed as i8*
T,AS = argtyp.parameters
actual_typ = LLVM.PointerType(convert(LLVMType, T), AS)
actual_arg = bitcast!(builder, arg, actual_typ)
elseif argtyp <: Ptr
T = eltype(argtyp)
if T === Nothing
T = Int8
end
actual_typ = LLVM.PointerType(convert(LLVMType, T))
actual_arg = if value_type(arg) isa LLVM.PointerType
# passed as i8* or ptr
bitcast!(builder, arg, actual_typ)
else
# passed as i64
inttoptr!(builder, arg, actual_typ)
end
elseif argtyp <: Bool
# passed as i8
T = eltype(argtyp)
actual_typ = LLVM.Int1Type()
actual_arg = trunc!(builder, arg, actual_typ)
else
actual_typ = convert(LLVMType, argtyp)
actual_arg = arg
end
push!(T_actual_args, actual_typ)
push!(actual_args, actual_arg)
end

str = globalstring_ptr!(builder, String(fmt); addrspace=AS.UniformConstant)
arg_types = Tuple{argspec...}

# invoke printf and return
printf_typ = LLVM.FunctionType(T_int32, [T_pint8]; vararg=true)
printf = LLVM.Function(mod, "printf", printf_typ)
push!(function_attributes(printf), EnumAttribute("nobuiltin"))
chars = call!(builder, printf_typ, printf, [str, actual_args...])
# pass the format string and the argument types as statically-known arguments, so that
# the IR generator is compiled once instead of for every format string
generate_llvmcall(printf_ir, Int32, Tuple{Val{fmt}, Type{arg_types}, argspec...},
Val(fmt), arg_types, arg_exprs...)
end

ret!(builder, chars)
function printf_ir(builder, fmt::Val, arg_types::Type{<:Tuple}, args...)
@nospecialize
T_int32 = LLVM.Int32Type()
T_pint8 = LLVM.PointerType(LLVM.Int8Type(), AS.UniformConstant)

# `printf` needs to be invoked very specifically, e.g., the format string needs
# to be a pointer to a string, and arguments need to match exactly what is
# expected by the format string, so we cannot rely on how the arguments to this
# function have been passed in (by `llvmcall`).
actual_args = LLVM.Value[]
for (arg, argtyp) in zip(args, fieldtypes(arg_types))
if argtyp <: LLVMPtr
# passed as i8*
T,AS = argtyp.parameters
actual_typ = LLVM.PointerType(convert(LLVMType, T), AS)
actual_arg = bitcast!(builder, arg, actual_typ)
elseif argtyp <: Ptr
T = eltype(argtyp)
if T === Nothing
T = Int8
end
actual_typ = LLVM.PointerType(convert(LLVMType, T))
actual_arg = if arg.value_type isa LLVM.PointerType
# passed as i8* or ptr
bitcast!(builder, arg, actual_typ)
else
# passed as i64
inttoptr!(builder, arg, actual_typ)
end
elseif argtyp <: Bool
# passed as i8
actual_typ = LLVM.Int1Type()
actual_arg = trunc!(builder, arg, actual_typ)
else
actual_arg = arg
end

call_function(llvm_f, Int32, Tuple{arg_types...}, arg_exprs...)
push!(actual_args, actual_arg)
end

fmt_str = String(typeof(fmt).parameters[1]::Symbol)
str = globalstring_ptr!(builder, fmt_str; addrspace=AS.UniformConstant)

# invoke printf and return
printf_typ = LLVM.FunctionType(T_int32, [T_pint8]; vararg=true)
printf = LLVM.Function(current_module(builder), "printf", printf_typ)
push!(printf.function_attributes, EnumAttribute(:nobuiltin))
call!(builder, printf_typ, printf, [str, actual_args...])
end


Expand Down
Loading