# This file is a part of Julia. License is MIT: https://julialang.org/license using Base: llvmcall using InteractiveUtils: code_llvm #function add1234(x::Tuple{Int32,Int32,Int32,Int32}) # llvmcall("""%3 = add <4 x i32> %1, %0 # ret <4 x i32> %3""", # Tuple{Int32,Int32,Int32,Int32}, # Tuple{Tuple{Int32,Int32,Int32,Int32}, # Tuple{Int32,Int32,Int32,Int32}}, # (Int32(1),Int32(2),Int32(3),Int32(4)), # x) #end # #function add1234(x::NTuple{4,Int64}) # llvmcall("""%3 = add <4 x i64> %1, %0 # ret <4 x i64> %3""",NTuple{4,Int64}, # Tuple{NTuple{4,Int64},NTuple{4,Int64}}, # (Int64(1),Int64(2),Int64(3),Int64(4)), # x) #end # function add1234(x::Tuple{Int32,Int32,Int32,Int32}) llvmcall("""%3 = extractvalue [4 x i32] %0, 0 %4 = extractvalue [4 x i32] %0, 1 %5 = extractvalue [4 x i32] %0, 2 %6 = extractvalue [4 x i32] %0, 3 %7 = extractvalue [4 x i32] %1, 0 %8 = extractvalue [4 x i32] %1, 1 %9 = extractvalue [4 x i32] %1, 2 %10 = extractvalue [4 x i32] %1, 3 %11 = add i32 %3, %7 %12 = add i32 %4, %8 %13 = add i32 %5, %9 %14 = add i32 %6, %10 %15 = insertvalue [4 x i32] undef, i32 %11, 0 %16 = insertvalue [4 x i32] %15, i32 %12, 1 %17 = insertvalue [4 x i32] %16, i32 %13, 2 %18 = insertvalue [4 x i32] %17, i32 %14, 3 ret [4 x i32] %18""",Tuple{Int32,Int32,Int32,Int32}, Tuple{Tuple{Int32,Int32,Int32,Int32},Tuple{Int32,Int32,Int32,Int32}}, (Int32(1),Int32(2),Int32(3),Int32(4)), x) end @test add1234(map(Int32,(2,3,4,5))) === map(Int32,(3,5,7,9)) #@test add1234(map(Int64,(2,3,4,5))) === map(Int64,(3,5,7,9)) # Test whether llvmcall escapes the function name correctly baremodule PlusTest using Base: llvmcall using Test using Base function +(x::Int32, y::Int32) llvmcall("""%3 = add i32 %1, %0 ret i32 %3""", Int32, Tuple{Int32, Int32}, x, y) end @test Int32(1) + Int32(2) == Int32(3) end # issue #11800 @test_throws ErrorException eval(Expr(:call,Core.Intrinsics.llvmcall, """%3 = add i32 %1, %0 ret i32 %3""", Int32, Tuple{Int32, Int32}, Int32(1), Int32(2))) # llvmcall must be compiled to be called #Since LLVM 18, LLVM does a best effort to automatically include the intrinsics function undeclared_ceil(x::Float64) llvmcall("""%2 = call double @llvm.ceil.f64(double %0) ret double %2""", Float64, Tuple{Float64}, x) end @test undeclared_ceil(4.2) == 5.0 @test undeclared_ceil(4.2) == 5.0 function declared_floor(x::Float64) llvmcall( ("""declare double @llvm.floor.f64(double) define double @entry(double) #0 { 1: %2 = call double @llvm.floor.f64(double %0) ret double %2 } attributes #0 = { alwaysinline } """, "entry"), Float64, Tuple{Float64}, x) end @test declared_floor(4.2) ≈ 4. ir = sprint(code_llvm, declared_floor, Tuple{Float64}) @test occursin("call double @llvm.floor.f64", ir) # should be inlined function doubly_declared_floor(x::Float64) llvmcall( ("""declare double @llvm.floor.f64(double) define double @entry(double) #0 { 1: %2 = call double @llvm.floor.f64(double %0) ret double %2 } attributes #0 = { alwaysinline } """, "entry"), Float64, Tuple{Float64}, x+1)-1 end @test doubly_declared_floor(4.2) ≈ 4. function doubly_declared2_trunc(x::Float64) a = llvmcall( ("""declare double @llvm.trunc.f64(double) define double @entry(double) #0 { 1: %2 = call double @llvm.trunc.f64(double %0) ret double %2 } attributes #0 = { alwaysinline } """, "entry"), Float64, Tuple{Float64}, x) b = llvmcall( ("""declare double @llvm.trunc.f64(double) define double @entry(double) #0 { 1: %2 = call double @llvm.trunc.f64(double %0) ret double %2 } attributes #0 = { alwaysinline } """, "entry"), Float64, Tuple{Float64}, x+1)-1 a + b end @test doubly_declared2_trunc(4.2) ≈ 8. # Test for proper mangling of external (C) functions function call_jl_errno() llvmcall( ("""declare i32 @jl_errno() define i32 @entry() #0 { 0: %r = call i32 @jl_errno() ret i32 %r } attributes #0 = { alwaysinline } """, "entry"),Int32,Tuple{}) end call_jl_errno() module ObjLoadTest using Base: llvmcall, @ccallable using Test didcall = false """ jl_the_callback() Sets the global didcall when it did the call """ @ccallable Cvoid function jl_the_callback() global didcall didcall = true nothing end @test_throws(ErrorException("@ccallable was already defined for this method name"), @eval @ccallable Cvoid jl_the_callback(not_the_method::Int) = "other") # Make sure everything up until here gets compiled @test jl_the_callback() === nothing @test jl_the_callback(1) == "other" didcall = false function do_the_call() llvmcall( ("""declare void @jl_the_callback() define void @entry() #0 { 0: call void @jl_the_callback() ret void } attributes #0 = { alwaysinline } """, "entry"),Cvoid,Tuple{}) end do_the_call() @test didcall end # Test for proper parenting local foo function foo() # this IR snippet triggers an optimization relying # on the llvmcall function having a parent module Base.llvmcall( """%1 = getelementptr i64, i64* null, i64 1 ret void""", Cvoid, Tuple{}) end code_llvm(devnull, foo, ()) module CcallableRetTypeTest using Base: llvmcall, @ccallable using Test @ccallable function jl_test_returns_float()::Float64 return 42 end function do_the_call() llvmcall( ("""declare double @jl_test_returns_float() define double @entry() #0 { 0: %1 = call double @jl_test_returns_float() ret double %1 } attributes #0 = { alwaysinline } """, "entry"),Float64,Tuple{}) end @test do_the_call() === 42.0 end # Issue #48093 - test that non-external globals are not deduplicated function kernel() Base.llvmcall((""" @shmem = internal global i8 0, align 8 define void @entry() { store i8 1, i8* @shmem ret void }""", "entry"), Cvoid, Tuple{}) Base.llvmcall((""" @shmem = internal global i8 0, align 8 define i8 @entry() { %1 = load i8, i8* @shmem ret i8 %1 }""", "entry"), UInt8, Tuple{}) end @test kernel() == 0x00 # If this test breaks, you've probably broken Cxx.jl - please check module LLVMCallFunctionTest using Base: llvmcall using Test const libllvmcalltest = "libllvmcalltest" const the_ir = unsafe_string(ccall((:MakeIdentityFunction, libllvmcalltest), Cstring, (Any,), Any)) @eval really_complicated_identity(x) = llvmcall(($(the_ir), "identity"), Any, Tuple{Any}, x) mutable struct boxed_struct end let x = boxed_struct() @test really_complicated_identity(x) === x end # Define two functions that each compute the address of a dedicated internal global variable. # The names of these globals are the same, so if their linkages are overwritten, then the # linker will merge the globals. Consequently, we can test that linkage is preserved by testing # that the addresses of the globals differ. The next few lines of code do just that. const the_other_ir1 = unsafe_string(ccall((:MakeLoadGlobalFunction, libllvmcalltest), Cstring, ())) const the_other_ir2 = unsafe_string(ccall((:MakeLoadGlobalFunction, libllvmcalltest), Cstring, ())) @eval global_value_address1() = llvmcall(($(the_other_ir1), "load_global_var"), Int64, Tuple{}) @eval global_value_address2() = llvmcall(($(the_other_ir2), "load_global_var"), Int64, Tuple{}) @test global_value_address1() != global_value_address2() end # issue 34166 f34166(x) = Base.llvmcall("ret i$(Sys.WORD_SIZE) %0", Int, (Int,), x) @test_throws ErrorException f34166(1) # Test that codegen can construct constant LLVMPtr #38864 struct MyStruct kern::UInt64 ptr::Core.LLVMPtr{UInt8,1} end MyStruct(kern) = MyStruct(kern, reinterpret(Core.LLVMPtr{UInt8,1}, 0)) MyStruct() = MyStruct(0) s = MyStruct() # ensure LLVMPtr properly subtypes @test eltype(supertype(Core.LLVMPtr{UInt8,1})) <: UInt8 @test s.kern == 0 @test reinterpret(Int, s.ptr) == 0