JuliaGPU / JuliaGPU/GPUCompiler.jl

Int128 codegen

Open
#101 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Julia
Stars
187
Forks
68
Avg merge
1d 12h
Merged PRs (30d)
28

Description

We currently sometimes emit `i128`:

```
julia> function kernel(A)
@inbounds A[1, 2] += 1
return
end
kernel (generic function with 1 method)

julia> @device_code_llvm @oneapi kernel(reshape(oneArray{Float32}(undef, (1,1))', 1, 1))
; CompilerJob of kernel kernel(Base.ReshapedArray{Float32,2,LinearAlgebra.Adjoint{Float32,oneDeviceArray{Float32,2,1}},Tuple{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64}}}) for GPUCompiler.SPIRVCompilerTarget
```

```llvm
define spir_kernel void @_Z18julia_kernel_1195413ReshapedArrayI7Float32Li2E7AdjointIS0_14oneDeviceArrayIS0_Li2ELi1EEE5TupleI27SignedMultiplicativeInverseI5Int64EEE({ { [1 x { [2 x i64], i8 addrspace(1)* }], [2 x i64], [1 x { i64, i64, i8, i8 }] } }* byval) local_unnamed_addr {
entry:
; @ REPL[82]:2 within `kernel'
; ┌ @ reshapedarray.jl:234 within `getindex'
; │┌ @ reshapedarray.jl:244 within `_unsafe_getindex'
; ││┌ @ abstractarray.jl:1894 within `_sub2ind'
; │││┌ @ abstractarray.jl:1910 within `_sub2ind_recurse'
; ││││┌ @ tuple.jl:24 within `getindex'
%1 = getelementptr inbounds { { [1 x { [2 x i64], i8 addrspace(1)* }], [2 x i64], [1 x { i64, i64, i8, i8 }] } }, { { [1 x { [2 x i64], i8 addrspace(1)* }], [2 x i64], [1 x { i64, i64, i8, i8 }] } }* %0, i64 0, i32 0, i32 1, i64 0
; ││││└
; ││││ @ abstractarray.jl:1911 within `_sub2ind_recurse'
; ││││┌ @ abstractarray.jl:1914 within `nextL'
; │││││┌ @ int.jl:87 within `*'
%2 = load i64, i64* %1, align 8
; ││└└└└
; ││ @ reshapedarray.jl:245 within `_unsafe_getindex'
; ││┌ @ reshapedarray.jl:218 within `ind2sub_rs'
; │││┌ @ reshapedarray.jl:221 within `_ind2sub_rs'
; ││││┌ @ multinverses.jl:152 within `divrem'
; │││││┌ @ multinverses.jl:138 within `div'
; ││││││┌ @ operators.jl:818 within `widen'
; │││││││┌ @ number.jl:7 within `convert'
; ││││││││┌ @ boot.jl:709 within `Int128'
; │││││││││┌ @ boot.jl:639 within `toInt128'
%3 = sext i64 %2 to i128
; ││││││└└└└
; ││││││┌ @ Base.jl:33 within `getproperty'
%4 = getelementptr inbounds { { [1 x { [2 x i64], i8 addrspace(1)* }], [2 x i64], [1 x { i64, i64, i8, i8 }] } }, { { [1 x { [2 x i64], i8 addrspace(1)* }], [2 x i64], [1 x { i64, i64, i8, i8 }] } }* %0, i64 0, i32 0, i32 2, i64 0, i32 1
; ││││││└
; ││││││┌ @ int.jl:920 within `*'
; │││││││┌ @ int.jl:476 within `rem'
%5 = load i64, i64* %4, align 8
%6 = sext i64 %5 to i128
; │││││││└
; │││││││ @ int.jl:922 within `*' @ int.jl:907
%7 = mul nsw i128 %6, %3
; ││││││└
; ││││││┌ @ int.jl:465 within `>>>' @ int.jl:457
%8 = lshr i128 %7, 64
; ││││││└
; ││││││┌ @ int.jl:471 within `rem'
%9 = trunc i128 %8 to i64
```

This is OK for platforms that know how to handle this, e.g., PTX decomposes the i128 into two i64's:

```
*** IR Dump After NVPTX specific alloca hoisting ***
define dso_local ptx_kernel void @_Z17julia_kernel_387613ReshapedArrayI7Float32Li2E7AdjointIS0_13CuDeviceArrayIS0_Li2ELi1EEE5TupleI27SignedMultiplicativeInverseI5Int64EEE({ [1 x { [2 x i64], i8 addrspace(1)* }], [2 x i64], [1 x { i64, i64, i8, i8 }] }* nocapture nonnull readonly byval dereferenceable(64) %0) local_unnamed_addr {
top:
%1 = addrspacecast { [1 x { [2 x i64], i8 addrspace(1)* }], [2 x i64], [1 x { i64, i64, i8, i8 }] }* %0 to { [1 x { [2 x i64], i8 addrspace(1)* }], [2 x i64], [1 x { i64, i64, i8, i8 }] } addrspace(101)*
%2 = load { [1 x { [2 x i64], i8 addrspace(1)* }], [2 x i64], [1 x { i64, i64, i8, i8 }] }, { [1 x { [2 x i64], i8 addrspace(1)* }], [2 x i64], [1 x { i64, i64, i8, i8 }] } addrspace(101)* %1
%.fca.0.0.0.0.extract = extractvalue { [1 x { [2 x i64], i8 addrspace(1)* }], [2 x i64], [1 x { i64, i64, i8, i8 }] } %2, 0, 0, 0, 0
%.fca.0.0.1.extract = extractvalue { [1 x { [2 x i64], i8 addrspace(1)* }], [2 x i64], [1 x { i64, i64, i8, i8 }] } %2, 0, 0, 1
%.fca.1.0.extract = extractvalue { [1 x { [2 x i64], i8 addrspace(1)* }], [2 x i64], [1 x { i64, i64, i8, i8 }] } %2, 1, 0
%.fca.2.0.0.extract = extractvalue { [1 x { [2 x i64], i8 addrspace(1)* }], [2 x i64], [1 x { i64, i64, i8, i8 }] } %2, 2, 0, 0
%.fca.2.0.1.extract = extractvalue { [1 x { [2 x i64], i8 addrspace(1)* }], [2 x i64], [1 x { i64, i64, i8, i8 }] } %2, 2, 0, 1
%.fca.2.0.2.extract = extractvalue { [1 x { [2 x i64], i8 addrspace(1)* }], [2 x i64], [1 x { i64, i64, i8, i8 }] } %2, 2, 0, 2
%.fca.2.0.3.extract = extractvalue { [1 x { [2 x i64], i8 addrspace(1)* }], [2 x i64], [1 x { i64, i64, i8, i8 }] } %2, 2, 0, 3
%3 = sext i64 %.fca.1.0.extract to i128
%4 = sext i64 %.fca.2.0.1.extract to i128
%5 = mul nsw i128 %4, %3
%6 = lshr i128 %5, 64
%7 = trunc i128 %6 to i64
%8 = sext i8 %.fca.2.0.2.extract to i64
%9 = mul i64 %.fca.1.0.extract, %8
%10 = add i64 %9, %7
%11 = icmp slt i64 %.fca.2.0.0.extract, 0
%12 = sub i64 0, %.fca.2.0.0.extract
%13 = select i1 %11, i64 %12, i64 %.fca.2.0.0.extract
%14 = icmp eq i64 %13, 1
%15 = mul i64 %.fca.2.0.0.extract, %.fca.1.0.extract
%16 = icmp ult i8 %.fca.2.0.3.extract, 63
%narrow = select i1 %16, i8 %.fca.2.0.3.extract, i8 63
%.v = zext i8 %narrow to i64
%17 = ashr i64 %10, %.v
%.lobit = lshr i64 %10, 63
%18 = add i64 %17, %.lobit
%19 = select i1 %14, i64 %15, i64 %18
%20 = mul i64 %19, %.fca.2.0.0.extract
%21 = sub i64 %.fca.1.0.extract, %20
%22 = icmp sgt i64 %.fca.0.0.0.0.extract, 0
%23 = select i1 %22, i64 %.fca.0.0.0.0.extract, i64 0
%24 = mul i64 %21, %23
%25 = add i64 %24, %19
%26 = bitcast i8 addrspace(1)* %.fca.0.0.1.extract to float addrspace(1)*
%27 = getelementptr inbounds float, float addrspace(1)* %26, i64 %25
%28 = load float, float addrspace(1)* %27, align 4
%29 = fadd float %28, 1.000000e+00
store float %29, float addrspace(1)* %27, align 4
ret void
}
# *** IR Dump After NVPTX DAG->DAG Pattern Instruction Selection ***:
# Machine code for function _Z17julia_kernel_387613ReshapedArrayI7Float32Li2E7AdjointIS0_13CuDeviceArrayIS0_Li2ELi1EEE5TupleI27SignedMultiplicativeInverseI5Int64EEE: IsSSA, TracksLiveness

bb.0.top:
%0:int64regs = LD_i64_asi 0, 4, 1, 0, 64, &_Z17julia_kernel_387613ReshapedArrayI7Float32Li2E7AdjointIS0_13CuDeviceArrayIS0_Li2ELi1EEE5TupleI27SignedMultiplicativeInverseI5Int64EEE_param_0, 16 :: (dereferenceable load 8 from %ir.1 + 16, addrspace 101)
%1:int64regs = LD_i64_avar 0, 4, 1, 0, 64, &_Z17julia_kernel_387613ReshapedArrayI7Float32Li2E7AdjointIS0_13CuDeviceArrayIS0_Li2ELi1EEE5TupleI27SignedMultiplicativeInverseI5Int64EEE_param_0 :: (dereferenceable load 8 from %ir.1, addrspace 101)
%2:int16regs = LD_i16_asi 0, 4, 1, 0, 8, &_Z17julia_kernel_387613ReshapedArrayI7Float32Li2E7AdjointIS0_13CuDeviceArrayIS0_Li2ELi1EEE5TupleI27SignedMultiplicativeInverseI5Int64EEE_param_0, 57 :: (dereferenceable load 1 from %ir.1 + 57, addrspace 101)
%3:int64regs = LD_i64_asi 0, 4, 1, 0, 64, &_Z17julia_kernel_387613ReshapedArrayI7Float32Li2E7AdjointIS0_13CuDeviceArrayIS0_Li2ELi1EEE5TupleI27SignedMultiplicativeInverseI5Int64EEE_param_0, 40 :: (dereferenceable load 8 from %ir.1 + 40, addrspace 101)
%4:int64regs = LD_i64_asi 0, 4, 1, 1, 8, &_Z17julia_kernel_387613ReshapedArrayI7Float32Li2E7AdjointIS0_13CuDeviceArrayIS0_Li2ELi1EEE5TupleI27SignedMultiplicativeInverseI5Int64EEE_param_0, 56 :: (dereferenceable load 1 from %ir.1 + 56, addrspace 101)
%5:int64regs = LD_i64_asi 0, 4, 1, 0, 64, &_Z17julia_kernel_387613ReshapedArrayI7Float32Li2E7AdjointIS0_13CuDeviceArrayIS0_Li2ELi1EEE5TupleI27SignedMultiplicativeInverseI5Int64EEE_param_0, 24 :: (dereferenceable load 8 from %ir.1 + 24, addrspace 101)
%6:int64regs = LD_i64_asi 0, 4, 1, 0, 64, &_Z17julia_kernel_387613ReshapedArrayI7Float32Li2E7AdjointIS0_13CuDeviceArrayIS0_Li2ELi1EEE5TupleI27SignedMultiplicativeInverseI5Int64EEE_param_0, 48 :: (dereferenceable load 8 from %ir.1 + 48, addrspace 101)
%7:int64regs = MULTHSi64rr killed %6:int64regs, %5:int64regs
%8:int64regs = MULTi64rr %5:int64regs, killed %4:int64regs
%9:int64regs = ADDi64rr killed %8:int64regs, killed %7:int64regs
%10:int64regs = anonymous_2233 %3:int64regs
%11:int1regs = SETP_s64ri killed %10:int64regs, 1, 0
%12:int64regs = MULTi64rr %3:int64regs, %5:int64regs
%13:int16regs = UMINi16ri killed %2:int16regs, 63
%14:int32regs = CVT_u32_u16 killed %13:int16regs, 0
%15:int64regs = SRAi64rr %9:int64regs, killed %14:int32regs
%16:int64regs = SRLi64ri %9:int64regs, 63
%17:int64regs = ADDi64rr killed %15:int64regs, killed %16:int64regs
%18:int64regs = SELP_b64rr killed %12:int64regs, killed %17:int64regs, killed %11:int1regs
%19:int64regs = MULTi64rr %18:int64regs, %3:int64regs
%20:int64regs = SUBi64rr %5:int64regs, killed %19:int64regs
%21:int64regs = SMAXi64ri killed %1:int64regs, 0
%22:int64regs = MULTi64rr killed %20:int64regs, killed %21:int64regs
%23:int64regs = ADDi64rr killed %22:int64regs, %18:int64regs
%24:int64regs = SHLi64ri killed %23:int64regs, 2
%25:int64regs = ADDi64rr killed %0:int64regs, killed %24:int64regs
%26:float32regs = LD_f32_areg_64 0, 1, 1, 2, 32, %25:int64regs :: (load 4 from %ir.27, addrspace 1)
%27:float32regs = FADD_rnf32ri killed %26:float32regs, float 1.000000e+00
ST_f32_areg_64 killed %27:float32regs, 0, 1, 1, 2, 32, %25:int64regs :: (store 4 into %ir.27, addrspace 1)
Return
```

On SPIR-V, not so much:

```
%u128 = OpTypeInt 128 0
...
%31 = OpLoad %ulong %30 Aligned 8
%32 = OpSConvert %u128 %31
%33 = OpIMul %u128 %32 %28
%35 = OpShiftRightLogical %u128 %33 %u128_
```

and this is not a legal type

```
┌ Error: Module compilation failed:

│ error: undefined reference to `__builtin_spirv_OpSConvert_i128_i64()'
│ undefined reference to `__builtin_spirv_OpUConvert_i64_i128()'

│ error: backend compiler failed build.
└ @ oneAPI.oneL0 ~/Julia/pkg/oneAPI/lib/level-zero/module.jl:49
ERROR: ZeError: error occurred when building module, see build log for details (code 1879048196, ZE_RESULT_ERROR_MODULE_BUILD_FAILURE)
```
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the SPIR-V output showing OpTypeInt 128 and the failure reported at oneL0 ~/Julia/pkg/oneAPI/lib/level-zero/module.jl:49. Trace how the kernel's Int128 operations reach SPIR-V code generation and compare this with the working PTX output. Done means the reproduced kernel produces a legal SPIR-V module without unsupported i128 conversion references.

Written by the indexing model from the issue text.

Assessment

Tech stack
julia
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.