diff --git a/base/base.jl b/base/base.jl index 9d9c41cac0fad..94393999c679f 100644 --- a/base/base.jl +++ b/base/base.jl @@ -142,8 +142,8 @@ end finalize(o::ANY) = ccall(:jl_finalize_th, Void, (Ptr{Void}, Any,), Core.getptls(), o) -gc(full::Bool=true) = ccall(:jl_gc_collect, Void, (Cint,), full) -gc_enable(on::Bool) = ccall(:jl_gc_enable, Cint, (Cint,), on)!=0 +gc(full::Bool=true) = ccall(:jl_gc_collect, Void, (Int32,), full) +gc_enable(on::Bool) = ccall(:jl_gc_enable, Int32, (Int32,), on) != 0 immutable Nullable{T} hasvalue::Bool diff --git a/base/c.jl b/base/c.jl index 09367b9709cc5..cdee897119bb1 100644 --- a/base/c.jl +++ b/base/c.jl @@ -27,18 +27,21 @@ else typealias Culong UInt typealias Cwchar_t Int32 end + """ Clong Equivalent to the native `signed long` c-type. """ Clong + """ Culong Equivalent to the native `unsigned long` c-type. """ Culong + """ Cwchar_t diff --git a/base/ctypes.jl b/base/ctypes.jl index b96189f42afa9..037cfd74ad638 100644 --- a/base/ctypes.jl +++ b/base/ctypes.jl @@ -9,78 +9,104 @@ Equivalent to the native `unsigned char` c-type (`UInt8`). """ typealias Cuchar UInt8 + + """ Cshort Equivalent to the native `signed short` c-type (`Int16`). """ typealias Cshort Int16 + + """ Cushort Equivalent to the native `unsigned short` c-type (`UInt16`). """ typealias Cushort UInt16 + + """ Cint Equivalent to the native `signed int` c-type (`Int32`). """ typealias Cint Int32 + + """ Cuint Equivalent to the native `unsigned int` c-type (`UInt32`). """ typealias Cuint UInt32 + + """ Cptrdiff_t Equivalent to the native `ptrdiff_t` c-type (`Int`). """ typealias Cptrdiff_t Int + + """ Csize_t Equivalent to the native `size_t` c-type (`UInt`). """ typealias Csize_t UInt + + """ Cssize_t Equivalent to the native `ssize_t` c-type. """ typealias Cssize_t Int + + """ Cintmax_t Equivalent to the native `intmax_t` c-type (`Int64`). """ typealias Cintmax_t Int64 + + """ Cuintmax_t Equivalent to the native `uintmax_t` c-type (`UInt64`). """ typealias Cuintmax_t UInt64 + + """ Clonglong Equivalent to the native `signed long long` c-type (`Int64`). """ typealias Clonglong Int64 + + """ Culonglong Equivalent to the native `unsigned long long` c-type (`UInt64`). """ typealias Culonglong UInt64 + + """ Cfloat Equivalent to the native `float` c-type (`Float32`). """ typealias Cfloat Float32 + + """ Cdouble diff --git a/base/essentials.jl b/base/essentials.jl index 44aef5b3d85be..006854f3c225a 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -129,11 +129,11 @@ end map(f::Function, a::Array{Any,1}) = Any[ f(a[i]) for i=1:length(a) ] function precompile(f::ANY, args::Tuple) - ccall(:jl_compile_hint, Cint, (Any,), Tuple{Core.Typeof(f), args...}) != 0 + ccall(:jl_compile_hint, Int32, (Any,), Tuple{Core.Typeof(f), args...}) != 0 end function precompile(argt::Type) - ccall(:jl_compile_hint, Cint, (Any,), argt) != 0 + ccall(:jl_compile_hint, Int32, (Any,), argt) != 0 end """ diff --git a/base/inference.jl b/base/inference.jl index 9998875ba483e..44a52fef1e5f2 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -1199,25 +1199,32 @@ function abstract_eval(e::ANY, vtypes::VarTable, sv::InferenceState) abstract_eval(e.args[1], vtypes, sv) t = Any elseif is(e.head, :foreigncall) + rt = e.args[2] + if isdefined(sv.linfo, :def) + tvars = sv.linfo.def.tvars + if tvars !== svec() + env = data_pointer_from_objref(sv.linfo.sparam_vals) + sizeof(Ptr{Void}) + rt = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), e.args[2], tvars, env) + end + end abstract_eval(e.args[1], vtypes, sv) - rt = abstract_eval(e.args[2], vtypes, sv) - for i = 3:length(e.args) + for i = 4:length(e.args) if abstract_eval(e.args[i], vtypes, sv) === Bottom rt = Bottom end end if rt === Bottom t = Bottom - elseif !isType(rt) - t = Any - else - t = rt.parameters[1] + elseif isa(rt, Type) + t = rt if isa(t, DataType) && is((t::DataType).name, Ref.name) t = t.parameters[1] if t === Any t = Bottom # a return type of Box{Any} is invalid end end + else + t = Any end elseif is(e.head, :static_parameter) n = e.args[1] @@ -2212,7 +2219,7 @@ end # replace slots 1:na with argexprs, static params with spvals, and increment # other slots by offset. -function substitute!(e::ANY, na, argexprs, spvals, offset) +function substitute!(e::ANY, na::Int, argexprs::Vector{Any}, tvars::ANY, spvals::Vector{Any}, offset::Int) if isa(e, Slot) if 1 <= e.id <= na ae = argexprs[e.id] @@ -2227,17 +2234,25 @@ function substitute!(e::ANY, na, argexprs, spvals, offset) return TypedSlot(e.id+offset, e.typ) end end - if isa(e,NewvarNode) - return NewvarNode(substitute!(e.slot, na, argexprs, spvals, offset)) + if isa(e, NewvarNode) + return NewvarNode(substitute!(e.slot, na, argexprs, tvars, spvals, offset)) end - if isa(e,Expr) + if isa(e, Expr) e = e::Expr head = e.head if head === :static_parameter return spvals[e.args[1]] + elseif head === :foreigncall + for i = 1:length(e.args) + if i == 2 || i == 3 + e.args[2] = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), e.args[2], tvars, spvals) + else + e.args[i] = substitute!(e.args[i], na, argexprs, tvars, spvals, offset) + end + end elseif !is_meta_expr_head(head) - for i=1:length(e.args) - e.args[i] = substitute!(e.args[i], na, argexprs, spvals, offset) + for i = 1:length(e.args) + e.args[i] = substitute!(e.args[i], na, argexprs, tvars, spvals, offset) end end end @@ -2598,14 +2613,14 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference return invoke_NF() end - na = method.nargs + na = Int(method.nargs) # check for vararg function isva = false if na > 0 && method.isva - @assert length(argexprs) >= na-1 + @assert length(argexprs) >= na - 1 # construct tuple-forming expression for argument tail vararg = mk_tuplecall(argexprs[na:end], sv) - argexprs = Any[argexprs[1:(na-1)]..., vararg] + argexprs = Any[argexprs[1:(na - 1)]..., vararg] isva = true elseif na != length(argexprs) # we have a method match only because an earlier @@ -2709,7 +2724,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference prelude_stmts = Any[] stmts_free = true # true = all entries of stmts are effect_free - for i=na:-1:1 # stmts_free needs to be calculated in reverse-argument order + for i = na:-1:1 # stmts_free needs to be calculated in reverse-argument order #args_i = args[i] aei = argexprs[i] aeitype = argtype = widenconst(exprtype(aei, sv.src, sv.mod)) @@ -2758,10 +2773,10 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference end # ok, substitute argument expressions for argument names in the body - body = substitute!(body, na, argexprs, spvals, length(sv.src.slotnames) - na) - append!(sv.src.slotnames, src.slotnames[na+1:end]) - append!(sv.src.slottypes, src.slottypes[na+1:end]) - append!(sv.src.slotflags, src.slotflags[na+1:end]) + body = substitute!(body, na, argexprs, method.tvars, spvals, length(sv.src.slotnames) - na) + append!(sv.src.slotnames, src.slotnames[(na + 1):end]) + append!(sv.src.slottypes, src.slottypes[(na + 1):end]) + append!(sv.src.slotflags, src.slotflags[(na + 1):end]) # make labels / goto statements unique # relocate inlining information diff --git a/base/pointer.jl b/base/pointer.jl index 2c885f55ee0a5..a424d688f8594 100644 --- a/base/pointer.jl +++ b/base/pointer.jl @@ -62,7 +62,7 @@ function unsafe_wrap{T,N}(::Union{Type{Array},Type{Array{T}},Type{Array{T,N}}}, end function unsafe_wrap{T}(::Union{Type{Array},Type{Array{T}},Type{Array{T,1}}}, p::Ptr{T}, d::Integer, own::Bool=false) - ccall(:jl_ptr_to_array_1d, Vector{T}, + ccall(:jl_ptr_to_array_1d, Array{T,1}, (Any, Ptr{Void}, Csize_t, Cint), Array{T,1}, p, d, own) end unsafe_wrap{N,I<:Integer}(Atype::Type, p::Ptr, dims::NTuple{N,I}, own::Bool=false) = @@ -93,8 +93,8 @@ and makes a copy of the data. """ unsafe_wrap(::Type{String}, p::Union{Ptr{UInt8},Ptr{Int8}}, len::Integer, own::Bool=false) = ccall(:jl_array_to_string, Ref{String}, (Any,), - ccall(:jl_ptr_to_array_1d, Vector{UInt8}, (Any, Ptr{UInt8}, Csize_t, Cint), - Vector{UInt8}, p, len, own)) + ccall(:jl_ptr_to_array_1d, Array{UInt8,1}, (Any, Ptr{UInt8}, Csize_t, Cint), + Array{UInt8,1}, p, len, own)) unsafe_wrap(::Type{String}, p::Union{Ptr{UInt8},Ptr{Int8}}, own::Bool=false) = unsafe_wrap(String, p, ccall(:strlen, Csize_t, (Ptr{UInt8},), p), own) diff --git a/base/reflection.jl b/base/reflection.jl index 05458dc728cb1..72aa6df6b5aa5 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -77,7 +77,7 @@ end Get an array of the names exported by a `Module`, with optionally more `Module` globals according to the additional parameters. """ -names(m::Module, all::Bool=false, imported::Bool=false) = sort!(ccall(:jl_module_names, Array{Symbol,1}, (Any,Cint,Cint), m, all, imported)) +names(m::Module, all::Bool=false, imported::Bool=false) = sort!(ccall(:jl_module_names, Array{Symbol,1}, (Any, Cint, Cint), m, all, imported)) isexported(m::Module, s::Symbol) = ccall(:jl_module_exports_p, Cint, (Any, Any), m, s) != 0 isdeprecated(m::Module, s::Symbol) = ccall(:jl_is_binding_deprecated, Cint, (Any, Any), m, s) != 0 @@ -364,12 +364,12 @@ function _methods_by_ftype(t::ANY, lim) return _methods(Any[tp...], length(tp), lim, []) end # XXX: the following can return incorrect answers that the above branch would have corrected - return ccall(:jl_matching_methods, Any, (Any,Cint,Cint), t, lim, 0) + return ccall(:jl_matching_methods, Any, (Any, Cint, Cint), t, lim, 0) end function _methods(t::Array,i,lim::Integer,matching::Array{Any,1}) if i == 0 - new = ccall(:jl_matching_methods, Any, (Any,Cint,Cint), Tuple{t...}, lim, 0) + new = ccall(:jl_matching_methods, Any, (Any, Cint, Cint), Tuple{t...}, lim, 0) new === false && return false append!(matching, new::Array{Any,1}) else @@ -432,7 +432,7 @@ methods(f::Core.Builtin) = MethodList(Method[], typeof(f).name.mt) function methods_including_ambiguous(f::ANY, t::ANY) ft = isa(f,Type) ? Type{f} : typeof(f) tt = isa(t,Type) ? Tuple{ft, t.parameters...} : Tuple{ft, t...} - ms = ccall(:jl_matching_methods, Any, (Any,Cint,Cint), tt, -1, 1)::Array{Any,1} + ms = ccall(:jl_matching_methods, Any, (Any, Cint, Cint), tt, -1, 1)::Array{Any,1} return MethodList(Method[m[3] for m in ms], typeof(f).name.mt) end function methods(f::ANY) @@ -521,7 +521,7 @@ function _dump_function(linfo::Core.MethodInstance, native::Bool, wrapper::Bool, if native str = ccall(:jl_dump_function_asm, Ref{String}, - (Ptr{Void}, Cint, Cstring), llvmf, 0, syntax) + (Ptr{Void}, Cint, Ptr{UInt8}), llvmf, 0, syntax) else str = ccall(:jl_dump_function_ir, Ref{String}, (Ptr{Void}, Bool, Bool), llvmf, strip_ir_metadata, dump_module) diff --git a/doc/manual/calling-c-and-fortran-code.rst b/doc/manual/calling-c-and-fortran-code.rst index ae4c5c18d6aa0..044090dfeb0dd 100644 --- a/doc/manual/calling-c-and-fortran-code.rst +++ b/doc/manual/calling-c-and-fortran-code.rst @@ -51,16 +51,20 @@ function, all inputs must be passed by reference. Finally, you can use :func:`ccall` to actually generate a call to the library function. Arguments to :func:`ccall` are as follows: -1. (:function, "library") pair (must be a constant, but see below). +1. A ``(:function, "library")`` pair, which must be written as a literal constant, + + OR + + a function pointer (for example, from :func:``dlsym``). 2. Return type (see below for mapping the declared C type to Julia) - - This argument will be evaluated at compile-time. + - This argument will be evaluated when the containing method is declared. 3. A tuple of input types. The input types must be written as a literal tuple, not a tuple-valued variable or expression. - - This argument will be evaluated at compile-time. + - This argument will be evaluated when the containing method is declared. 4. The following arguments, if any, are the actual argument values passed to the function. @@ -68,7 +72,7 @@ library function. Arguments to :func:`ccall` are as follows: As a complete but simple example, the following calls the ``clock`` function from the standard C library:: - julia> t = ccall( (:clock, "libc"), Int32, ()) + julia> t = ccall((:clock, "libc"), Int32, ()) 2292761 julia> t @@ -136,12 +140,12 @@ Here is a slightly more complex example that discovers the local machine's hostname:: function gethostname() - hostname = Array{UInt8}(128) - ccall((:gethostname, "libc"), Int32, - (Ptr{UInt8}, Csize_t), - hostname, sizeof(hostname)) - hostname[end] = 0; # ensure null-termination - return unsafe_string(pointer(hostname)) + hostname = Array{UInt8}(128) + ccall((:gethostname, "libc"), Int32, + (Ptr{UInt8}, Csize_t), + hostname, sizeof(hostname)) + hostname[end] = 0; # ensure null-termination + return unsafe_string(pointer(hostname)) end This example first allocates an array of bytes, then calls the C library @@ -324,10 +328,11 @@ There are several special types to be aware of, as no other type can be defined Exactly corresponds to the ``complex double`` type in C (or ``COMPLEX*16`` in Fortran). ``Signed`` - Exactly corresponds to the ``signed`` type annotation in C (or any ``INTEGER`` type in Fortran). Any Julia type that is not a subtype of ``Signed`` is assumed to be unsigned. + Exactly corresponds to the ``signed`` type annotation in C (or any ``INTEGER`` type in Fortran). + Any Julia type that is not a subtype of ``Signed`` is assumed to be unsigned. ``Ref{T}`` - Behaves like a ``Ptr{T}`` that owns its memory. + Behaves like a ``Ptr{T}`` that manages its memory via the Julia GC. ``Array{T,N}`` When an array is passed to C as a ``Ptr{T}`` argument, it is @@ -349,7 +354,8 @@ There are several special types to be aware of, as no other type can be defined On all systems we currently support, basic C/C++ value types may be translated to Julia types as follows. Every C type also has a corresponding -Julia type with the same name, prefixed by C. This can help for writing portable code (and remembering that an ``int`` in C is not the same as an ``Int`` in Julia). +Julia type with the same name, prefixed by C. This can help for writing portable code +(and remembering that an ``int`` in C is not the same as an ``Int`` in Julia). **System Independent:** @@ -481,7 +487,7 @@ C name Standard Julia Alias Julia Base Type A return type of ``Union{}`` means the function will not return i.e. C++11 ``[[noreturn]]`` or C11 ``_Noreturn`` (e.g. ``jl_throw`` or ``longjmp``). Do not use this for functions that return - no value (``void``) but do return. + no value (``void``) but do return, use ``Void`` instead. .. note:: @@ -533,30 +539,40 @@ the field that will have the greatest size (potentially including padding). When translating your fields to Julia, declare the Julia field to be only of that type. -Arrays of parameters must be expanded manually, currently -(either inline, or in an immutable helper type). For example:: +Arrays of parameters can be written as an NTuple. For example:: in C: struct B { int A[3]; }; - b_a_2 = B.A[2]; + b_a_2 = B.A[1]; in Julia: - immutable B_A - A_1::Cint - A_2::Cint - A_3::Cint - end type B - A::B_A + A::NTuple{3, A} end - b_a_2 = B.A.(2) + b_a_2 = B.A[2] -Arrays of unknown size are not supported. +Arrays of unknown size (C99-complaint variable length structs specified by `[]` or `[0]`) are not supported. In the future, some of these restrictions may be reduced or eliminated. + +Type Parameters +~~~~~~~~~~~~~~~ + +The static parameters of the function may be used as type parameters in the ``ccall`` signature, +as long as they don't affect the layout of the type. +For example, ``f{T}(x::T) = ccall(:valid, Ptr{T}, (Ptr{T},), x)`` +is valid, since ``Ptr`` is always a word-size bitstype. +But, ``g{T}(x::T) = ccall(:notvalid, T, (T,), x)`` +is not valid, since the type layout of T is not known statically. + +This may sound like a strange restriction, +but remember that since C is not a dynamic language like Julia, +its functions can only accept a function signature with a statically-known fixed signature. + + SIMD Values ~~~~~~~~~~~ @@ -809,12 +825,12 @@ Here is a simple example of a C wrapper that returns a ``Ptr`` type:: # gsl_permutation * gsl_permutation_alloc (size_t n); function permutation_alloc(n::Integer) output_ptr = ccall( - (:gsl_permutation_alloc, :libgsl), #name of C function and library - Ptr{gsl_permutation}, #output type - (Csize_t,), #tuple of input types - n #name of Julia variable to pass in + (:gsl_permutation_alloc, :libgsl), # name of C function and library + Ptr{gsl_permutation}, # output type + (Csize_t,), # tuple of input types + n # name of Julia variable to pass in ) - if output_ptr==C_NULL # Could not allocate memory + if output_ptr == C_NULL # Could not allocate memory throw(OutOfMemoryError()) end return output_ptr @@ -846,10 +862,10 @@ Here is a second example wrapping the corresponding destructor:: # void gsl_permutation_free (gsl_permutation * p); function permutation_free(p::Ref{gsl_permutation}) ccall( - (:gsl_permutation_free, :libgsl), #name of C function and library - Void, #output type - (Ref{gsl_permutation},), #tuple of input types - p #name of Julia variable to pass in + (:gsl_permutation_free, :libgsl), # name of C function and library + Void, # output type + (Ref{gsl_permutation},), # tuple of input types + p # name of Julia variable to pass in ) end @@ -875,15 +891,19 @@ Here is a third example passing Julia arrays:: # int gsl_sf_bessel_Jn_array (int nmin, int nmax, double x, # double result_array[]) function sf_bessel_Jn_array(nmin::Integer, nmax::Integer, x::Real) - if nmax` unpacks the Julia pointer to a Julia array data -structure into a form understandable by C. +Julia, not C. The implicit call +to :func:`Base.cconvert(Ref{Cdouble}, result_array) ` +unpacks the Julia pointer to a Julia array data structure into a form understandable by C. Note that for this code to work correctly, ``result_array`` must be declared to be of type ``Ref{Cdouble}`` and not ``Ptr{Cdouble}``. The memory is managed by @@ -942,7 +962,7 @@ A ``(name, library)`` function specification must be a constant expression. However, it is possible to use computed values as function names by staging through ``eval`` as follows:: - @eval ccall(($(string("a","b")),"lib"), ... + @eval ccall(($(string("a", "b")), "lib"), ... This expression constructs a name using ``string``, then substitutes this name into a new :func:`ccall` expression, which is then evaluated. Keep in mind that @@ -969,7 +989,7 @@ then cache it in a global variable for that session. For example:: macro dlsym(func, lib) z, zlocal = gensym(string(func)), gensym() - eval(current_module(),:(global $z = C_NULL)) + eval(current_module(), :(global $z = C_NULL)) z = esc(z) quote let $zlocal::Ptr{Void} = $z::Ptr{Void} @@ -1026,7 +1046,7 @@ Global variables exported by native libraries can be accessed by name using the identical to that used by :func:`ccall`, and a type describing the value stored in the variable:: - julia> cglobal((:errno,:libc), Int32) + julia> cglobal((:errno, :libc), Int32) Ptr{Int32} @0x00007f418d0816b8 The result is a pointer giving the address of the value. The value can be diff --git a/src/alloc.c b/src/alloc.c index 6eaa4b8fd3add..a2e1a939873b1 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -267,8 +267,31 @@ JL_DLLEXPORT jl_value_t *jl_new_struct_uninit(jl_datatype_t *type) } +// type constructor ----------------------------------------------------------- + +JL_DLLEXPORT jl_value_t *jl_new_type_constructor(jl_svec_t *p, jl_value_t *body) +{ + jl_ptls_t ptls = jl_get_ptls_states(); +#ifndef NDEBUG + size_t i, np = jl_svec_len(p); + for (i = 0; i < np; i++) { + jl_tvar_t *tv = (jl_tvar_t*)jl_svecref(p, i); + assert(jl_is_typevar(tv) && !tv->bound); + } +#endif + jl_typector_t *tc = + (jl_typector_t*)jl_gc_alloc(ptls, sizeof(jl_typector_t), + jl_typector_type); + tc->parameters = p; + tc->body = body; + return (jl_value_t*)tc; +} + + +// method constructors -------------------------------------------------------- + extern jl_value_t *jl_builtin_getfield; -jl_value_t *jl_resolve_globals(jl_value_t *expr, jl_module_t *module) +jl_value_t *jl_resolve_globals(jl_value_t *expr, jl_module_t *module, jl_svec_t *sparam_vals) { if (jl_is_symbol(expr)) { if (module == NULL) @@ -281,6 +304,7 @@ jl_value_t *jl_resolve_globals(jl_value_t *expr, jl_module_t *module) e->head == global_sym || e->head == quote_sym || e->head == inert_sym || e->head == line_sym || e->head == meta_sym || e->head == inbounds_sym || e->head == boundscheck_sym || e->head == simdloop_sym) { + // ignore these } else { if (e->head == call_sym && jl_expr_nargs(e) == 3 && @@ -318,13 +342,38 @@ jl_value_t *jl_resolve_globals(jl_value_t *expr, jl_module_t *module) } } } - size_t i = 0; + size_t i = 0, nargs = jl_array_len(e->args); + if (e->head == foreigncall_sym) { + JL_NARGSV(ccall method definition, 3); // (fptr, rt, at) + jl_value_t *rt = jl_exprarg(e, 1); + jl_value_t *at = jl_exprarg(e, 2); + if (!jl_is_type(rt)) { + rt = jl_interpret_toplevel_expr_in(module, rt, NULL, sparam_vals); + jl_exprargset(e, 1, rt); + } + if (!jl_is_svec(at)) { + at = jl_interpret_toplevel_expr_in(module, at, NULL, sparam_vals); + jl_exprargset(e, 2, at); + } + if (jl_is_svec(rt)) + jl_error("ccall: missing return type"); + JL_TYPECHK(ccall method definition, type, rt); + JL_TYPECHK(ccall method definition, simplevector, at); + size_t nargt = jl_svec_len(at); + int isVa = (nargt > 0 && jl_is_vararg_type(jl_svecref(at, nargt - 1))); + if (nargs % 2 == 0) // ignore calling-convention arg, if present + nargs -= 1; + if ((!isVa && nargt != (nargs - 2) / 2) || + ( isVa && nargt - 1 > (nargs - 2) / 2)) + jl_error("ccall: wrong number of arguments to C function"); + } if (e->head == method_sym || e->head == abstracttype_sym || e->head == compositetype_sym || - e->head == bitstype_sym || e->head == module_sym) + e->head == bitstype_sym || e->head == module_sym) { i++; - for (; i < jl_array_len(e->args); i++) { + } + for (; i < nargs; i++) { // TODO: this should be making a copy, not mutating the source - jl_exprargset(e, i, jl_resolve_globals(jl_exprarg(e, i), module)); + jl_exprargset(e, i, jl_resolve_globals(jl_exprarg(e, i), module, sparam_vals)); } } } @@ -538,7 +587,7 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo) jl_array_t *stmts = (jl_array_t*)func->code; for (i = 0, l = jl_array_len(stmts); i < l; i++) { - jl_array_ptr_set(stmts, i, jl_resolve_globals(jl_array_ptr_ref(stmts, i), linfo->def->module)); + jl_array_ptr_set(stmts, i, jl_resolve_globals(jl_array_ptr_ref(stmts, i), linfo->def->module, env)); } ptls->in_pure_callback = last_in; jl_lineno = last_lineno; @@ -590,11 +639,15 @@ JL_DLLEXPORT void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) } m->called = called; + jl_array_t *copy = NULL; + jl_svec_t *sparam_vals = m->tvars; + if (!jl_is_svec(sparam_vals)) + sparam_vals = jl_svec1(sparam_vals); + JL_GC_PUSH2(©, &sparam_vals); assert(jl_typeis(src->code, jl_array_any_type)); jl_array_t *stmts = (jl_array_t*)src->code; size_t i, n = jl_array_len(stmts); - jl_array_t *copy = jl_alloc_vec_any(n); - JL_GC_PUSH1(©); + copy = jl_alloc_vec_any(n); int set_lineno = 0; for (i = 0; i < n; i++) { jl_value_t *st = jl_array_ptr_ref(stmts, i); @@ -607,7 +660,7 @@ JL_DLLEXPORT void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) } } else { - st = jl_resolve_globals(st, m->module); + st = jl_resolve_globals(st, m->module, sparam_vals); } jl_array_ptr_set(copy, i, st); } @@ -640,7 +693,6 @@ JL_DLLEXPORT jl_method_t *jl_new_method_uninit(void) m->isstaged = 0; m->isva = 0; m->nargs = 0; - m->needs_sparam_vals_ducttape = 2; m->traced = 0; JL_MUTEX_INIT(&m->writelock); return m; @@ -1175,27 +1227,6 @@ JL_DLLEXPORT jl_datatype_t *jl_new_bitstype(jl_value_t *name, jl_datatype_t *sup return bt; } -// type constructor ----------------------------------------------------------- - -JL_DLLEXPORT jl_value_t *jl_new_type_constructor(jl_svec_t *p, jl_value_t *body) -{ - jl_ptls_t ptls = jl_get_ptls_states(); -#ifndef NDEBUG - size_t i, np = jl_svec_len(p); - for (i = 0; i < np; i++) { - jl_tvar_t *tv = (jl_tvar_t*)jl_svecref(p, i); - assert(jl_is_typevar(tv) && !tv->bound); - } -#endif - jl_typector_t *tc = - (jl_typector_t*)jl_gc_alloc(ptls, sizeof(jl_typector_t), - jl_typector_type); - tc->parameters = p; - tc->body = body; - return (jl_value_t*)tc; -} - - // bits constructors ---------------------------------------------------------- #define BOXN_FUNC(nb,nw) \ diff --git a/src/ccall.cpp b/src/ccall.cpp index d17506e9127e2..ec107610d7219 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -395,46 +395,28 @@ static bool is_native_simd_type(jl_datatype_t *dt) { typedef ABI_LLVMLayout DefaultAbiState; #endif - +// basic type widening and cast conversions static Value *llvm_type_rewrite( - Value *v, Type *from_type, Type *target_type, - bool tojulia, /* only matters if byref is set (declares the direction of the byref attribute) */ - bool byref, /* only applies to arguments, set false for return values -- effectively the same as jl_cgval_t.ispointer() */ + Value *v, Type *target_type, bool issigned, /* determines whether an integer value should be zero or sign extended */ jl_codectx_t *ctx) { - if (v->getType() == T_void) - return UndefValue::get(target_type); // convert undef (unreachable) -> undef (target_type) - - if (byref) { - if (tojulia) { - Type *ptarget_type = PointerType::get(target_type, 0); - if (v->getType() != ptarget_type) - v = builder.CreatePointerCast(v, ptarget_type); - return builder.CreateAlignedLoad(v, 1); // unknown alignment from C - } - else { - // julia_to_native should already have done the alloca and store - if (v->getType() != target_type) - v = builder.CreatePointerCast(v, target_type); - return v; - } - } - assert(v->getType() == from_type); - - if (target_type == from_type) { + Type *from_type = v->getType(); + if (target_type == from_type) return v; - } + + if (from_type == T_void || isa(v)) + return UndefValue::get(target_type); // convert undef (unreachable) -> undef (target_type) assert(from_type->isPointerTy() == target_type->isPointerTy()); // expect that all ABIs consider all pointers to be equivalent - if (target_type->isPointerTy()) { - return builder.CreatePointerCast(v, target_type); - } + if (target_type->isPointerTy()) + return emit_bitcast(v, target_type); // simple integer and float widening & conversion cases - if (from_type->getPrimitiveSizeInBits() > 0 && target_type->getPrimitiveSizeInBits() == from_type->getPrimitiveSizeInBits()) { + if (from_type->getPrimitiveSizeInBits() > 0 && + target_type->getPrimitiveSizeInBits() == from_type->getPrimitiveSizeInBits()) return emit_bitcast(v, target_type); - } + if (target_type->isFloatingPointTy() && from_type->isFloatingPointTy()) { if (target_type->getPrimitiveSizeInBits() > from_type->getPrimitiveSizeInBits()) return builder.CreateFPExt(v, target_type); @@ -443,6 +425,7 @@ static Value *llvm_type_rewrite( else return v; } + if (target_type->isIntegerTy() && from_type->isIntegerTy()) { if (issigned) return builder.CreateSExtOrTrunc(v, target_type); @@ -464,11 +447,11 @@ static Value *llvm_type_rewrite( #endif if (DL.getTypeAllocSize(target_type) >= DL.getTypeAllocSize(from_type)) { to = emit_static_alloca(target_type, ctx); - from = builder.CreatePointerCast(to, from_type->getPointerTo()); + from = emit_bitcast(to, from_type->getPointerTo()); } else { from = emit_static_alloca(from_type, ctx); - to = builder.CreatePointerCast(from, target_type->getPointerTo()); + to = emit_bitcast(from, target_type->getPointerTo()); } builder.CreateStore(v, from); return builder.CreateLoad(to); @@ -476,36 +459,11 @@ static Value *llvm_type_rewrite( // --- argument passing and scratch space utilities --- -// Emit code to convert argument to form expected by C ABI -// to = desired LLVM type -// jlto = Julia type of formal argument -// jvinfo = value of actual argument -static Value *julia_to_native(Type *to, bool toboxed, jl_value_t *jlto, const jl_cgval_t &jvinfo, - bool addressOf, bool byRef, bool inReg, - bool tojulia, int argn, jl_codectx_t *ctx, - bool *needStackRestore) +static void typeassert_input(const jl_cgval_t &jvinfo, jl_value_t *jlto, int argn, bool addressOf, jl_codectx_t *ctx) { - // We're passing Any - if (toboxed) { - assert(!addressOf && !byRef); // don't expect any ABI to pass pointers by pointer - return boxed(jvinfo, ctx); - } - assert(jl_is_leaf_type(jlto)); - - jl_value_t *ety = jlto; - if (addressOf) { - if (!jl_is_cpointer_type(jlto)) { - emit_error("ccall: & on argument was not matched by Ptr{T} argument type", ctx); - return UndefValue::get(T_void); - } - ety = jl_tparam0(jlto); - if (jlto == (jl_value_t*)jl_voidpointer_type) - ety = jvinfo.typ; // skip the type-check - assert(to->isPointerTy()); - } - if (jvinfo.typ != ety && ety != (jl_value_t*)jl_any_type) { - if (!addressOf && ety == (jl_value_t*)jl_voidpointer_type) { - // allow a bit more flexibility for what can be passed to (void*) due to Ref{T} conversion behavior below + if (jvinfo.typ != jlto && jlto != (jl_value_t*)jl_any_type) { + if (!addressOf && jlto == (jl_value_t*)jl_voidpointer_type) { + // allow a bit more flexibility for what can be passed to (void*) due to Ref{T} conversion behavior in input if (!jl_is_cpointer_type(jvinfo.typ)) { // emit a typecheck, if not statically known to be correct std::stringstream msg; @@ -519,14 +477,32 @@ static Value *julia_to_native(Type *to, bool toboxed, jl_value_t *jlto, const jl std::stringstream msg; msg << "ccall argument "; msg << argn; - emit_typecheck(jvinfo, ety, msg.str(), ctx); + emit_typecheck(jvinfo, jlto, msg.str(), ctx); } } +} + +static Value *julia_to_address(Type *to, jl_value_t *jlto, const jl_cgval_t &jvinfo, + int argn, jl_codectx_t *ctx, bool *needStackRestore) +{ + assert(jl_is_datatype(jlto) && julia_struct_has_layout((jl_datatype_t*)jlto)); + + if (!jl_is_cpointer_type(jlto) || !to->isPointerTy()) { + emit_error("ccall: & on argument was not matched by Ptr{T} argument type", ctx); + return UndefValue::get(to); + } - if (!addressOf && !byRef) - return emit_unbox(to, jvinfo, ety); + jl_value_t *ety; + if (jlto == (jl_value_t*)jl_voidpointer_type) { + ety = jvinfo.typ; // skip the type-check + } + else { + ety = jl_tparam0(jlto); + typeassert_input(jvinfo, ety, argn, true, ctx); + } + assert(to->isPointerTy()); - if (addressOf && jvinfo.isboxed) { + if (jvinfo.isboxed) { if (!jl_is_abstracttype(ety)) { if (jl_is_mutable_datatype(ety)) { // no copy, just reference the data field @@ -536,7 +512,7 @@ static Value *julia_to_native(Type *to, bool toboxed, jl_value_t *jlto, const jl // yes copy Value *nbytes; AllocaInst *ai; - if (jl_is_leaf_type(ety)) { + if (jl_is_leaf_type(ety) || jl_is_bitstype(ety)) { int nb = jl_datatype_size(ety); nbytes = ConstantInt::get(T_int32, nb); ai = emit_static_alloca(T_int8, nb, ctx); @@ -547,8 +523,7 @@ static Value *julia_to_native(Type *to, bool toboxed, jl_value_t *jlto, const jl *needStackRestore = true; } ai->setAlignment(16); - prepare_call( - builder.CreateMemCpy(ai, data_pointer(jvinfo, ctx, T_pint8), nbytes, sizeof(void*))->getCalledValue()); // minimum gc-alignment in julia is pointer size + builder.CreateMemCpy(ai, data_pointer(jvinfo, ctx, T_pint8), nbytes, sizeof(void*)); // minimum gc-alignment in julia is pointer size return emit_bitcast(ai, to); } } @@ -567,7 +542,7 @@ static Value *julia_to_native(Type *to, bool toboxed, jl_value_t *jlto, const jl Value *nbytes = emit_datatype_size(jvt); AllocaInst *ai = builder.CreateAlloca(T_int8, nbytes); ai->setAlignment(16); - prepare_call(builder.CreateMemCpy(ai, data_pointer(jvinfo, ctx, T_pint8), nbytes, sizeof(void*))->getCalledValue()); // minimum gc-alignment in julia is pointer size + builder.CreateMemCpy(ai, data_pointer(jvinfo, ctx, T_pint8), nbytes, sizeof(void*)); // minimum gc-alignment in julia is pointer size Value *p2 = emit_bitcast(ai, to); builder.CreateBr(afterBB); builder.SetInsertPoint(afterBB); @@ -577,18 +552,57 @@ static Value *julia_to_native(Type *to, bool toboxed, jl_value_t *jlto, const jl return p; } + Type *slottype = julia_struct_to_llvm(jvinfo.typ, NULL); + // pass the address of an alloca'd thing, not a box + // since those are immutable. + Value *slot = emit_static_alloca(slottype, ctx); + if (!jvinfo.ispointer()) { + builder.CreateStore(emit_unbox(slottype, jvinfo, ety), slot); + } + else { + builder.CreateMemCpy(slot, + data_pointer(jvinfo, ctx, slot->getType()), + (uint64_t)jl_datatype_size(ety), + (uint64_t)((jl_datatype_t*)ety)->layout->alignment); + mark_gc_use(jvinfo); + } + if (slot->getType() != to) + slot = emit_bitcast(slot, to); + return slot; +} + + +// Emit code to convert argument to form expected by C ABI +// to = desired LLVM type +// jlto = Julia type of formal argument +// jvinfo = value of actual argument +static Value *julia_to_native(Type *to, bool toboxed, jl_value_t *jlto, + const jl_cgval_t &jvinfo, + bool byRef, int argn, jl_codectx_t *ctx, + bool *needStackRestore) +{ + // We're passing Any + if (toboxed) { + assert(!byRef); // don't expect any ABI to pass pointers by pointer + return boxed(jvinfo, ctx); + } + assert(jl_is_datatype(jlto) && julia_struct_has_layout((jl_datatype_t*)jlto)); + + typeassert_input(jvinfo, jlto, argn, false, ctx); + if (!byRef) + return emit_unbox(to, jvinfo, jlto); + // pass the address of an alloca'd thing, not a box // since those are immutable. - if (addressOf) - to = to->getContainedType(0); Value *slot = emit_static_alloca(to, ctx); if (!jvinfo.ispointer()) { - builder.CreateStore(emit_unbox(to, jvinfo, ety), slot); + builder.CreateStore(emit_unbox(to, jvinfo, jlto), slot); } else { - prepare_call(builder.CreateMemCpy(slot, data_pointer(jvinfo, ctx, slot->getType()), - (uint64_t)jl_datatype_size(ety), - (uint64_t)((jl_datatype_t*)ety)->layout->alignment)->getCalledValue()); + builder.CreateMemCpy(slot, + data_pointer(jvinfo, ctx, slot->getType()), + (uint64_t)jl_datatype_size(jlto), + (uint64_t)((jl_datatype_t*)jlto)->layout->alignment); mark_gc_use(jvinfo); } return slot; @@ -976,10 +990,10 @@ static jl_cgval_t emit_llvmcall(jl_value_t **args, size_t nargs, jl_codectx_t *c jl_cgval_t &arg = argv[i]; arg = emit_expr(argi, ctx); - Value *v = julia_to_native(t, toboxed, tti, arg, false, false, false, false, i, ctx, NULL); + Value *v = julia_to_native(t, toboxed, tti, arg, false, i, ctx, NULL); // make sure args are rooted bool issigned = jl_signed_type && jl_subtype(tti, (jl_value_t*)jl_signed_type, 0); - argvals[i] = llvm_type_rewrite(v, t, t, false, false, issigned, ctx); + argvals[i] = llvm_type_rewrite(v, t, issigned, ctx); } Function *f; @@ -1128,14 +1142,28 @@ static jl_cgval_t emit_llvmcall(jl_value_t **args, size_t nargs, jl_codectx_t *c static jl_cgval_t mark_or_box_ccall_result(Value *result, bool isboxed, jl_value_t *rt_expr, jl_value_t *rt, bool static_rt, jl_codectx_t *ctx) { if (!static_rt) { + assert(!isboxed && ctx->spvals_ptr && jl_is_datatype(rt)); // box if concrete type was not statically known - assert(rt == (jl_value_t*)jl_voidpointer_type); - Value *runtime_bt = boxed(emit_expr(rt_expr, ctx), ctx); - int nb = sizeof(void*); - // TODO: can this be tighter than tbaa_value? - return mark_julia_type( - init_bits_value(emit_allocobj(ctx, nb, runtime_bt), result, tbaa_value), - true, (jl_value_t*)jl_pointer_type, ctx); + Value *args[3]; + args[0] = literal_pointer_val(rt); + args[1] = literal_pointer_val((jl_value_t*)ctx->linfo->def->tvars); + args[2] = builder.CreateInBoundsGEP( + LLVM37_param(T_pjlvalue) + emit_bitcast(ctx->spvals_ptr, T_ppjlvalue), + ConstantInt::get(T_size, sizeof(jl_svec_t) / sizeof(jl_value_t*))); + Value *runtime_dt = builder.CreateCall(prepare_call(jlapplytype_func), ArrayRef(args)); + // TODO: is this leaf check actually necessary, or is it structurally guaranteed? + emit_leafcheck(runtime_dt, "ccall: return type must be a leaf DataType", ctx); +#if JL_LLVM_VERSION >= 30600 + const DataLayout &DL = jl_ExecutionEngine->getDataLayout(); +#else + const DataLayout &DL = *jl_ExecutionEngine->getDataLayout(); +#endif + unsigned nb = DL.getTypeStoreSize(result->getType()); + MDNode *tbaa = jl_is_mutable(rt) ? tbaa_mutab : tbaa_immut; + Value *strct = emit_allocobj(ctx, nb, runtime_dt); + init_bits_value(strct, result, tbaa); + return mark_julia_type(strct, true, rt, ctx); } return mark_julia_type(result, isboxed, rt, ctx); } @@ -1150,6 +1178,7 @@ class function_sig_t { std::vector byRefList; // vector of "byref" parameters (vararg is the last item, if applicable) AttributeSet attrs; // vector of function call site attributes (vararg is the last item, if applicable) Type *lrt; // input parameter of the llvm return type (from julia_struct_to_llvm) + bool retboxed; // input parameter indicating whether lrt is jl_value_t* Type *prt; // out parameter of the llvm return type for the function signature int sret; // out parameter for indicating whether return value has been moved to the first argument position std::string err_msg; @@ -1158,20 +1187,25 @@ class function_sig_t { FunctionType *functype; jl_svec_t *at; // svec of julia argument types jl_value_t *rt; // julia return type + jl_svec_t *tvars; // UnionAll environment for `at` and `rt` size_t nargs; // number of actual arguments (can be different from the size of at when varargs) size_t isVa; - function_sig_t(Type *lrt, jl_value_t *rt, jl_svec_t *at, size_t nargs, size_t isVa, CallingConv::ID cc, bool llvmcall) - : fargt_vasig(NULL), lrt(lrt), prt(NULL), sret(0), cc(cc), llvmcall(llvmcall), functype(NULL), at(at), rt(rt), nargs(nargs), isVa(isVa) + function_sig_t(Type *lrt, jl_value_t *rt, bool retboxed, jl_svec_t *at, jl_svec_t *tvars, size_t nargs, size_t isVa, CallingConv::ID cc, bool llvmcall) + : fargt_vasig(NULL), lrt(lrt), retboxed(retboxed), + prt(NULL), sret(0), cc(cc), llvmcall(llvmcall), + functype(NULL), at(at), rt(rt), tvars(tvars), + nargs(nargs), isVa(isVa) { err_msg = generate_func_sig(); - functype = FunctionType::get(sret ? T_void : prt, fargt_sig, isVa); + if (err_msg.empty()) + functype = FunctionType::get(sret ? T_void : prt, fargt_sig, isVa); } jl_cgval_t emit_a_ccall(const native_sym_arg_t &symarg, size_t nargt, std::vector &addressOf, jl_cgval_t *argv, SmallVector &gc_uses, - jl_value_t *rt_expr, bool static_rt, bool retboxed, + jl_value_t *rt_expr, bool static_rt, jl_codectx_t *ctx); private: @@ -1192,7 +1226,7 @@ std::string generate_func_sig() prt = lrt = T_void; } else { - if (!jl_is_datatype(rt) || ((jl_datatype_t*)rt)->layout == NULL || jl_is_cpointer_type(rt) || jl_is_array_type(rt)) { + if (!jl_is_datatype(rt) || ((jl_datatype_t*)rt)->layout == NULL || jl_is_cpointer_type(rt) || jl_is_array_type(rt) || retboxed) { prt = lrt; // passed as pointer } else if (abi->use_sret((jl_datatype_t*)rt)) { @@ -1215,7 +1249,7 @@ std::string generate_func_sig() size_t i; bool current_isVa = false; - for(i = 0; i < nargt;) { + for (i = 0; i < nargt;) { jl_value_t *tti = jl_svecref(at, i); if (jl_is_vararg_type(tti)) { current_isVa = true; @@ -1232,8 +1266,6 @@ std::string generate_func_sig() isboxed = false; } else { - if (jl_is_cpointer_type(tti) && jl_is_typevar(jl_tparam0(tti))) - jl_error("ccall: argument type Ptr should have an element type, not Ptr{T}"); if (jl_is_bitstype(tti)) { // see pull req #978. need to annotate signext/zeroext for // small integer arguments. @@ -1250,7 +1282,7 @@ std::string generate_func_sig() if (t == NULL || t == T_void) { std::stringstream msg; msg << "ccall: the type of argument "; - msg << i+1; + msg << (i + 1); msg << " doesn't correspond to a C type"; return msg.str(); } @@ -1344,60 +1376,63 @@ static std::pair convert_cconv(jl_sym_t *lhd) jl_errorf("ccall: invalid calling convention %s", jl_symbol_name(lhd)); } -static const std::string verify_ccall_sig(size_t nargs, jl_value_t *&rt, jl_value_t *at, const char *funcName, - size_t &nargt, bool &isVa) +static const std::string verify_ccall_sig(size_t nargs, jl_value_t *&rt, jl_value_t *at, + jl_svec_t *tvars, jl_svec_t *sparam_vals, const char *funcName, + size_t &nargt, bool &isVa, Type *&lrt, bool &retboxed, bool &static_rt) { - if (jl_is_svec(rt)) { - std::string msg = "in "; - msg += funcName; - msg += ": ccall: missing return type"; - jl_error(msg.c_str()); // TODO: this leaks memory - } - if (jl_is_cpointer_type(rt) && jl_is_typevar(jl_tparam0(rt))) - jl_error("ccall: return type Ptr should have an element type, not Ptr{_<:T}"); - - if (jl_is_abstract_ref_type(rt)) { - if (jl_tparam0(rt) == (jl_value_t*)jl_any_type) - jl_error("ccall: return type Ref{Any} is invalid. use Ptr{Any} instead."); - rt = (jl_value_t*)jl_any_type; // convert return type to jl_value_t* - } + assert(rt && !jl_is_abstract_ref_type(rt)); + JL_TYPECHK(ccall, type, rt); + JL_TYPECHK(ccall, simplevector, at); if (jl_is_array_type(rt)) { // `Array` used as return type just returns a julia object reference rt = (jl_value_t*)jl_any_type; } - JL_TYPECHK(ccall, type, rt); - JL_TYPECHK(ccall, simplevector, at); + lrt = julia_struct_to_llvm(rt, &retboxed); + if (lrt == NULL) + return "ccall: return type doesn't correspond to a C type"; - isVa = false; - nargt = jl_svec_len(at); - size_t i; - for (i = 0; i < nargt; i++) { - jl_value_t *tti = jl_svecref(at, i); - if (jl_is_cpointer_type(tti) && jl_is_typevar(jl_tparam0(tti))) { - return "ccall: argument type Ptr should have an element type, Ptr{T}"; + // is return type fully statically known? + if (tvars == jl_emptysvec) { + static_rt = true; + } + else { + static_rt = retboxed || !jl_has_typevars(rt); + if (!static_rt && sparam_vals != NULL) { + assert((jl_is_svec(tvars) ? jl_svec_len(tvars) : 1) == jl_svec_len(sparam_vals)); + rt = jl_instantiate_type_in_env(rt, (jl_value_t*)tvars, jl_svec_data(sparam_vals)); + static_rt = true; + } + } + + if (!retboxed && static_rt) { + if (!jl_is_leaf_type(rt)) { + if (jl_is_cpointer_type(rt)) + return "ccall: return type Ptr should have an element type (not Ptr{_<:T})"; + else if (rt != jl_bottom_type) + return "ccall: return type must be a leaf DataType"; } - if (jl_is_vararg_type(tti)) - isVa = true; } + nargt = jl_svec_len(at); + isVa = (nargt > 0 && jl_is_vararg_type(jl_svecref(at, nargt - 1))); if ((!isVa && nargt != (nargs - 2) / 2) || ( isVa && nargt - 1 > (nargs - 2) / 2)) - jl_error("ccall: wrong number of arguments to C function"); + return "ccall: wrong number of arguments to C function"; return ""; } -// Expr(:ccall, pointer, rettype, (argtypes...), args...) +// Expr(:foreigncall, pointer, rettype, (argtypes...), args...) static jl_cgval_t emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) { - jl_ptls_t ptls = jl_get_ptls_states(); JL_NARGSV(ccall, 3); - jl_value_t *rt = NULL, *at = NULL; + args -= 1; + jl_value_t *rt = args[2]; + jl_value_t *at = args[3]; native_sym_arg_t symarg = {}; JL_GC_PUSH3(&rt, &at, &symarg.gcroot); - args -= 1; CallingConv::ID cc = CallingConv::C; bool llvmcall = false; @@ -1420,99 +1455,74 @@ static jl_cgval_t emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) return jl_cgval_t(); } - jl_value_t *rtt_ = expr_type(args[2], ctx); - bool static_rt = true; // is return type fully statically known? - if (jl_is_type_type(rtt_) && jl_is_leaf_type(jl_tparam0(rtt_))) { - rt = jl_tparam0(rtt_); - } - else { - rt = try_eval(args[2], ctx, NULL); - if (rt == NULL) { - static_rt = false; - if (jl_is_type_type(rtt_)) { - if (jl_subtype(jl_tparam0(rtt_), (jl_value_t*)jl_pointer_type, 0)) { - // substitute Ptr{Void} for statically-unknown pointer type - rt = (jl_value_t*)jl_voidpointer_type; - } - else if (jl_subtype(jl_tparam0(rtt_), (jl_value_t*)jl_array_type, 0)) { - // `Array` used as return type just returns a julia object reference - rt = (jl_value_t*)jl_any_type; - static_rt = true; + jl_svec_t *tvars = ctx->linfo->def ? ctx->linfo->def->tvars : jl_emptysvec; + if (jl_is_abstract_ref_type(rt)) { + // emit verification that the tparam for Ref isn't Any or a TypeVar + jl_value_t *ref = jl_tparam0(rt); + bool always_error = false; + if (ref == (jl_value_t*)jl_any_type) { + always_error = true; + } + else if (jl_is_typevar(ref)) { + always_error = true; + if (tvars != jl_emptysvec) { + int i, ntv; + jl_value_t **tv; + if (jl_is_svec(tvars)) { + tv = jl_svec_data(tvars); + ntv = jl_svec_len(tvars); } - else if (jl_is_typevar(jl_tparam0(rtt_)) && jl_is_abstract_ref_type(((jl_tvar_t*)jl_tparam0(rtt_))->ub)) { - // `Ref{T}` used as return type just returns T (from a jl_value_t*) - rt = (jl_value_t*)jl_any_type; - static_rt = true; + else { + tv = (jl_value_t**)&tvars; + ntv = 1; } - } - if (rt == NULL) { - if (jl_is_expr(args[2])) { - jl_expr_t *rtexpr = (jl_expr_t*)args[2]; - if (rtexpr->head == call_sym && jl_expr_nargs(rtexpr) == 4 && - static_eval(jl_exprarg(rtexpr, 0), ctx, true, false) == jl_builtin_apply_type && - static_eval(jl_exprarg(rtexpr, 1), ctx, true, false) == (jl_value_t*)jl_array_type) { - // `Array` used as return type just returns a julia object reference - rt = (jl_value_t*)jl_any_type; - static_rt = true; - } - else if (rtexpr->head == call_sym && jl_expr_nargs(rtexpr) == 3 && - static_eval(jl_exprarg(rtexpr, 0), ctx, true, false) == jl_builtin_apply_type && - static_eval(jl_exprarg(rtexpr, 1), ctx, true, false) == (jl_value_t*)jl_pointer_type) { - // substitute Ptr{Void} for statically-unknown pointer type - rt = (jl_value_t*)jl_voidpointer_type; + for (i = 0; i < ntv; i++) + if (tv[i] == ref) + break; + if (i < ntv) { + jl_cgval_t runtime_sp = emit_sparam(i, ctx); + if (runtime_sp.constant) { + if (runtime_sp.constant != (jl_value_t*)jl_any_type) + always_error = false; } - else if (rtexpr->head == call_sym && jl_expr_nargs(rtexpr) == 3 && - static_eval(jl_exprarg(rtexpr, 0), ctx, true, false) == jl_builtin_apply_type && - static_eval(jl_exprarg(rtexpr, 1), ctx, true, false) == (jl_value_t*)jl_ref_type) { - // `Ref{T}` used as return type just returns T (from a jl_value_t*) - rt = (jl_value_t*)jl_any_type; - static_rt = true; + else { + Value *notany = builder.CreateICmpNE( + boxed(runtime_sp, ctx, false), + literal_pointer_val((jl_value_t*)jl_any_type)); + error_unless(notany, "ccall: return type Ref{Any} is invalid. use Ptr{Any} instead.", ctx); + always_error = false; } } } - if (rt == NULL) { - if (ptls->exception_in_transit && - jl_typeis(ptls->exception_in_transit, - jl_undefvarerror_type) && - jl_is_symbol(args[2])) { - std::string msg = "ccall return type undefined: " + - std::string(jl_symbol_name((jl_sym_t*)args[2])); - emit_error(msg.c_str(), ctx); - JL_GC_POP(); - return jl_cgval_t(); - } - emit_error("error interpreting ccall return type", ctx); - JL_GC_POP(); - return jl_cgval_t(); - } } - } - - at = try_eval(args[3], ctx, "error interpreting ccall argument tuple"); - if (at == NULL) { - JL_GC_POP(); - return jl_cgval_t(); + if (always_error) { + emit_error("ccall: return type Ref{Any} is invalid. use Ptr{Any} instead.", ctx); + JL_GC_POP(); + return jl_cgval_t(); + } + rt = (jl_value_t*)jl_any_type; // convert return type to jl_value_t* } // some sanity checking and check whether there's a vararg bool isVa; size_t nargt; - std::string err = verify_ccall_sig(/* inputs: */ nargs, rt, at, ctx->funcName.c_str(), - /* outputs: */ nargt, isVa); + Type *lrt; + bool retboxed; + bool static_rt; + std::string err = verify_ccall_sig( + /* inputs: */ + nargs, rt, at, tvars, + ctx->spvals_ptr == NULL ? ctx->linfo->sparam_vals : NULL, + ctx->funcName.c_str(), + /* outputs: */ + nargt, isVa, lrt, retboxed, static_rt); if (!err.empty()) { emit_error(err, ctx); JL_GC_POP(); return jl_cgval_t(); } - - bool retboxed; - Type *lrt = julia_struct_to_llvm(rt, &retboxed); - if (lrt == NULL) { - emit_error("ccall: return type doesn't correspond to a C type", ctx); - JL_GC_POP(); - return jl_cgval_t(); - } - + if (rt != args[2] && rt != (jl_value_t*)jl_any_type) + jl_add_method_root(ctx, rt); // some special functions if (fptr == (void(*)(void))&jl_array_ptr || @@ -1739,12 +1749,12 @@ static jl_cgval_t emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) push_gc_use(gc_uses, arg_root); } - function_sig_t sig(lrt, rt, (jl_svec_t*)at, (nargs - 3) / 2, isVa, cc, llvmcall); + function_sig_t sig(lrt, rt, retboxed, (jl_svec_t*)at, tvars, (nargs - 3) / 2, isVa, cc, llvmcall); jl_cgval_t retval = sig.emit_a_ccall( symarg, nargt, addressOf, argv, gc_uses, - args[2], static_rt, retboxed, + args[2], static_rt, ctx); JL_GC_POP(); return retval; @@ -1754,7 +1764,7 @@ jl_cgval_t function_sig_t::emit_a_ccall( const native_sym_arg_t &symarg, size_t nargt, std::vector &addressOf, jl_cgval_t *argv, SmallVector &gc_uses, - jl_value_t *rt_expr, bool static_rt, bool retboxed, + jl_value_t *rt_expr, bool static_rt, jl_codectx_t *ctx) { if (!err_msg.empty()) { @@ -1766,14 +1776,16 @@ jl_cgval_t function_sig_t::emit_a_ccall( BasicBlock::InstListType &instList = builder.GetInsertBlock()->getInstList(); Instruction *savespot = instList.empty() ? NULL : &instList.back(); + //jl_svec_t *tvars = ctx->linfo->def ? ctx->linfo->def->tvars : jl_emptysvec; + //unsigned ntvars = jl_is_svec(tvars) ? jl_svec_len(tvars) : 1; bool needStackRestore = false; Value **argvals = (Value**) alloca((nargs + sret) * sizeof(Value*)); for (size_t ai = 0; ai < nargs; ai++) { // Current C function parameter Type *largty; // LLVM type of the current parameter bool toboxed; - jl_value_t *jargty; // Julia type of the current parameter bool byRef, inReg; // Argument attributes + jl_value_t *jargty; // Julia type of the current parameter if (isVa && ai >= nargt - 1) { largty = fargt.at(nargt - 1); toboxed = fargt_isboxed.at(nargt - 1); @@ -1788,28 +1800,57 @@ jl_cgval_t function_sig_t::emit_a_ccall( byRef = byRefList.at(ai); inReg = inRegList.at(ai); } - bool argAddressOf = addressOf.at(ai); - + Type *pargty = ai + sret < fargt_sig.size() ? fargt_sig.at(ai + sret) : fargt_vasig; jl_cgval_t &arg = argv[ai]; - if (jl_is_abstract_ref_type(jargty)) { - if (argAddressOf) { + + // if we know the function sparams, try to fill those in now + // so that the julia_to_native type checks are more likely to be doable (e.g. leaf types) at compile-time + jl_value_t *jargty_in_env = jargty; + if (ctx->spvals_ptr == NULL && !toboxed && tvars != jl_emptysvec && jl_has_typevars(jargty)) { + assert((jl_is_svec(tvars) ? jl_svec_len(tvars) : 1) == jl_svec_len(ctx->linfo->sparam_vals)); + jargty_in_env = jl_instantiate_type_in_env(jargty_in_env, (jl_value_t*)tvars, jl_svec_data(ctx->linfo->sparam_vals)); + if (jargty_in_env != jargty) + jl_add_method_root(ctx, jargty_in_env); + } + + Value *v; + if (!addressOf.at(ai)) { + if (jl_is_abstract_ref_type(jargty)) { + if (!jl_is_cpointer_type(arg.typ)) { + emit_cpointercheck(arg, "ccall: argument to Ref{T} is not a pointer", ctx); + arg.typ = (jl_value_t*)jl_voidpointer_type; + arg.isboxed = false; + } + jargty_in_env = (jl_value_t*)jl_voidpointer_type; + } + + v = julia_to_native(largty, toboxed, jargty_in_env, arg, byRef, + ai + 1, ctx, &needStackRestore); + bool issigned = jl_signed_type && jl_subtype(jargty, (jl_value_t*)jl_signed_type, 0); + if (byRef) { + // julia_to_native should already have done the alloca and store + assert(v->getType() == pargty); + } + else { + v = llvm_type_rewrite(v, pargty, issigned, ctx); + } + } + else { + if (jl_is_abstract_ref_type(jargty)) { emit_error("ccall: & on a Ref{T} argument is invalid", ctx); + JL_GC_POP(); return jl_cgval_t(); } - if (!jl_is_cpointer_type(arg.typ)) { - emit_cpointercheck(arg, "ccall: argument to Ref{T} is not a pointer", ctx); - arg.typ = (jl_value_t*)jl_voidpointer_type; - arg.isboxed = false; - } - jargty = (jl_value_t*)jl_voidpointer_type; + assert(!toboxed && !byRef); + v = julia_to_address(largty, jargty_in_env, arg, + ai + 1, ctx, &needStackRestore); } - - Value *v = julia_to_native(largty, toboxed, jargty, arg, argAddressOf, byRef, inReg, - false, ai + 1, ctx, &needStackRestore); - bool issigned = jl_signed_type && jl_subtype(jargty, (jl_value_t*)jl_signed_type, 0); - argvals[ai + sret] = llvm_type_rewrite(v, largty, - ai + sret < fargt_sig.size() ? fargt_sig.at(ai + sret) : fargt_vasig, - false, byRef, issigned, ctx); + if (isa(v)) { + JL_GC_POP(); + return jl_cgval_t(); + } + assert(v->getType() == pargty); + argvals[ai + sret] = v; } Value *result = NULL; @@ -1937,50 +1978,62 @@ jl_cgval_t function_sig_t::emit_a_ccall( if (rt == jl_bottom_type) { // Do this after we marked all the GC uses. CreateTrap(builder); + return jl_cgval_t(); } + // Finally we need to box the result into julia type // However, if we have already created a box for the return // type because the ABI required us to pass a pointer (sret), // then we do not need to do this. - if (!sret) { - Type *jlrt = julia_type_to_llvm(rt, &retboxed); // compute the real "julian" return type and update retboxed + bool jlretboxed; + if (retboxed) { + assert(!sret); + jlretboxed = true; + } + else if (sret) { + jlretboxed = sretboxed; + if (!jlretboxed) + result = builder.CreateLoad(result); // something alloca'd above + } + else { + Type *jlrt = julia_type_to_llvm(rt, &jlretboxed); // compute the real "julian" return type and compute whether it is boxed if (type_is_ghost(jlrt)) { return ghostValue(rt); } - else if (lrt->isStructTy() && retboxed) { - assert(jl_is_structtype(rt)); - jl_cgval_t newst = emit_new_struct(rt, 1, NULL, ctx); // emit a new, empty struct - assert(newst.typ != NULL && "Type was not concrete"); - assert(newst.isboxed); - size_t rtsz = jl_datatype_size(rt); - assert(rtsz > 0); - int boxalign = jl_gc_alignment(rtsz); + else if (jl_is_datatype(rt) && jl_is_datatype_singleton((jl_datatype_t*)rt)) { + return mark_julia_const(((jl_datatype_t*)rt)->instance); + } + else if (jlretboxed && !retboxed) { + assert(jl_is_datatype(rt)); + if (static_rt) { + Value *runtime_bt = literal_pointer_val(rt); + size_t rtsz = jl_datatype_size(rt); + assert(rtsz > 0); + Value *strct = emit_allocobj(ctx, rtsz, runtime_bt); + int boxalign = jl_gc_alignment(rtsz); #ifndef NDEBUG #if JL_LLVM_VERSION >= 30600 - const DataLayout &DL = jl_ExecutionEngine->getDataLayout(); + const DataLayout &DL = jl_ExecutionEngine->getDataLayout(); #else - const DataLayout &DL = *jl_ExecutionEngine->getDataLayout(); + const DataLayout &DL = *jl_ExecutionEngine->getDataLayout(); #endif - // ARM and AArch64 can use a LLVM type larger than the julia - // type. However, the LLVM type size should be no larger than - // the GC allocation size. (multiple of `sizeof(void*)`) - assert(DL.getTypeStoreSize(lrt) <= LLT_ALIGN(jl_datatype_size(rt), - boxalign)); + // ARM and AArch64 can use a LLVM type larger than the julia + // type. However, the LLVM type size should be no larger than + // the GC allocation size. (multiple of `sizeof(void*)`) + assert(DL.getTypeStoreSize(lrt) <= LLT_ALIGN(rtsz, boxalign)); #endif - // copy the data from the return value to the new struct - tbaa_decorate(newst.tbaa, builder.CreateAlignedStore(result, emit_bitcast(newst.V, prt->getPointerTo()), boxalign)); - return newst; + // copy the data from the return value to the new struct + MDNode *tbaa = jl_is_mutable(rt) ? tbaa_mutab : tbaa_immut; + init_bits_value(strct, result, tbaa, boxalign); + return mark_julia_type(strct, true, rt, ctx); + } + jlretboxed = false; // trigger mark_or_box_ccall_result to build the runtime box } - else if (jlrt != prt) { - assert(lrt == jlrt); // jl_struct_to_llvm and julia_type_to_llvm should only differ for concrete types, per the case above - result = llvm_type_rewrite(result, prt, jlrt, true, false, false, ctx); + else if (lrt != prt) { + assert(jlrt == lrt || !lrt->isStructTy()); // julia_type_to_llvm and julia_struct_to_llvm should be returning the same StructType + result = llvm_type_rewrite(result, lrt, false, ctx); } } - else { - retboxed = sretboxed; - if (!retboxed) - result = builder.CreateLoad(result); // something alloca'd above - } - return mark_or_box_ccall_result(result, retboxed, rt_expr, rt, static_rt, ctx); + return mark_or_box_ccall_result(result, jlretboxed, rt_expr, rt, static_rt, ctx); } diff --git a/src/ccalltest.c b/src/ccalltest.c index 9f7d632458f33..56d75a306a5a1 100644 --- a/src/ccalltest.c +++ b/src/ccalltest.c @@ -90,6 +90,7 @@ JL_DLLEXPORT complex_t *cptest(complex_t *a) { } JL_DLLEXPORT complex_t *cptest_static(complex_t *a) { + if (verbose) fprintf(stderr,"%" PRIjint " + %" PRIjint " i\n", a->real, a->imag); complex_t *b = (complex_t*)malloc(sizeof(complex_t)); b->real = a->real; b->imag = a->imag; diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 2a15ba6e3c17a..234acdcb0dee3 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -323,48 +323,67 @@ JL_DLLEXPORT Type *julia_type_to_llvm(jl_value_t *jt, bool *isboxed) { // this function converts a Julia Type into the equivalent LLVM type if (isboxed) *isboxed = false; - if (jt == (jl_value_t*)jl_bool_type) return T_int8; - if (jt == (jl_value_t*)jl_bottom_type) return T_void; - if (!jl_is_leaf_type(jt)) { - if (isboxed) *isboxed = true; - return T_pjlvalue; - } - if (jl_is_cpointer_type(jt)) { - Type *lt = julia_type_to_llvm(jl_tparam0(jt)); - if (lt == NULL) - return NULL; + if (jt == (jl_value_t*)jl_bottom_type) + return T_void; + if (jl_is_leaf_type(jt)) { + if ((jl_is_bitstype(jt) || jl_isbits(jt))) { + if (jl_datatype_nbits(jt) == 0) + return T_void; + Type *t = julia_struct_to_llvm(jt, isboxed); + assert(t != NULL); + return t; + } + } + if (isboxed) *isboxed = true; + return T_pjlvalue; +} +} + +// converts a julia bitstype into the equivalent LLVM bitstype +static Type *bitstype_to_llvm(jl_value_t *bt) +{ + assert(jl_is_bitstype(bt)); + if (bt == (jl_value_t*)jl_bool_type) + return T_int8; + if (bt == (jl_value_t*)jl_long_type) + return T_size; + if (jl_is_cpointer_type(bt)) { + Type *lt = julia_type_to_llvm(jl_tparam0(bt)); if (lt == T_void) return T_pint8; return PointerType::get(lt, 0); } - if (jl_is_bitstype(jt)) { - if (jt == (jl_value_t*)jl_long_type) - return T_size; - int nb = jl_datatype_size(jt); - if (jl_is_floattype(jt)) { + int nb = jl_datatype_size(bt); + if (jl_is_floattype(bt)) { #ifndef DISABLE_FLOAT16 - if (nb == 2) - return T_float16; - else + if (nb == 2) + return T_float16; + else #endif - if (nb == 4) - return T_float32; - else if (nb == 8) - return T_float64; - else if (nb == 16) - return T_float128; - } - return Type::getIntNTy(jl_LLVMContext, jl_datatype_nbits(jt)); + if (nb == 4) + return T_float32; + else if (nb == 8) + return T_float64; + else if (nb == 16) + return T_float128; } - if (jl_isbits(jt)) { - if (jl_datatype_nbits(jt) == 0) { - return T_void; - } - return julia_struct_to_llvm(jt, isboxed); - } - if (isboxed) *isboxed = true; - return T_pjlvalue; + return Type::getIntNTy(jl_LLVMContext, nb * 8); } + +// compute whether all subtypes of this type have the same layout +// (which is equivalent to asking whether the types of any of the +// fields depend on any of the unbound parameters of the type) +static bool julia_struct_has_layout(jl_datatype_t *dt) +{ + if (dt->layout || dt->struct_decl || jl_is_bitstype(dt) || jl_isbits(dt)) + return true; + size_t i, ntypes = jl_datatype_nfields(dt); + for (i = 0; i < ntypes; i++) { + jl_value_t *ty = jl_svecref(dt->types, i); + if (jl_has_typevars_from(ty, dt->parameters)) + return false; + } + return true; } static Type *julia_struct_to_llvm(jl_value_t *jt, bool *isboxed) @@ -372,45 +391,62 @@ static Type *julia_struct_to_llvm(jl_value_t *jt, bool *isboxed) // this function converts a Julia Type into the equivalent LLVM struct // use this where C-compatible (unboxed) structs are desired // use julia_type_to_llvm directly when you want to preserve Julia's type semantics - bool isTuple = jl_is_tuple_type(jt); if (isboxed) *isboxed = false; + if (jt == (jl_value_t*)jl_bottom_type) + return T_void; + if (jl_is_bitstype(jt)) + return bitstype_to_llvm(jt); + bool isTuple = jl_is_tuple_type(jt); if ((isTuple || jl_is_structtype(jt)) && !jl_is_array_type(jt)) { - if (!jl_is_leaf_type(jt)) - return NULL; jl_datatype_t *jst = (jl_datatype_t*)jt; if (jst->struct_decl == NULL) { - size_t ntypes = jl_datatype_nfields(jst); - if (ntypes == 0 || jl_datatype_nbits(jst) == 0) + size_t i, ntypes = jl_svec_len(jst->types); + if (ntypes == 0 || (jst->layout && jl_datatype_nbits(jst) == 0)) return T_void; + if (!julia_struct_has_layout(jst)) + return NULL; StructType *structdecl; if (!isTuple) { structdecl = StructType::create(jl_LLVMContext, jl_symbol_name(jst->name->name)); jst->struct_decl = structdecl; } std::vector latypes(0); - size_t i; bool isarray = true; bool isvector = true; - jl_value_t* jlasttype = NULL; + jl_value_t *jlasttype = NULL; Type *lasttype = NULL; - for(i = 0; i < ntypes; i++) { + bool allghost = true; + for (i = 0; i < ntypes; i++) { jl_value_t *ty = jl_svecref(jst->types, i); - if (jlasttype!=NULL && ty!=jlasttype) + if (jlasttype != NULL && ty != jlasttype) isvector = false; jlasttype = ty; + bool isptr; + if (jst->layout) + isptr = jl_field_isptr(jst, i); + else // compute what jl_compute_field_offsets would say + isptr = jl_isbits(ty) && jl_is_leaf_type(ty) && ((jl_datatype_t*)ty)->layout; Type *lty; - if (jl_field_isptr(jst, i)) + if (isptr) lty = T_pjlvalue; + else if (ty == (jl_value_t*)jl_bool_type) + lty = T_int8; else - lty = ty==(jl_value_t*)jl_bool_type ? T_int8 : julia_type_to_llvm(ty); + lty = julia_type_to_llvm(ty); if (lasttype != NULL && lasttype != lty) isarray = false; lasttype = lty; if (type_is_ghost(lty)) lty = NoopType; + else + allghost = false; latypes.push_back(lty); } - if (!isTuple) { + if (allghost) { + assert(jst->layout == NULL); // otherwise should have been caught above + jst->struct_decl = T_void; + } + else if (!isTuple) { if (jl_is_vecelement_type(jt)) // VecElement type is unwrapped in LLVM jst->struct_decl = latypes[0]; @@ -419,31 +455,34 @@ static Type *julia_struct_to_llvm(jl_value_t *jt, bool *isboxed) } else { if (isarray && lasttype != T_int1 && !type_is_ghost(lasttype)) { - if (isvector && jl_special_vector_alignment(ntypes, jlasttype)!=0) + if (isvector && jl_special_vector_alignment(ntypes, jlasttype) != 0) jst->struct_decl = VectorType::get(lasttype, ntypes); else jst->struct_decl = ArrayType::get(lasttype, ntypes); } else { - jst->struct_decl = StructType::get(jl_LLVMContext,ArrayRef(&latypes[0],ntypes)); + jst->struct_decl = StructType::get(jl_LLVMContext, ArrayRef(&latypes[0], ntypes)); } } #ifndef NDEBUG // If LLVM and Julia disagree about alignment, much trouble ensues, so check it! - const DataLayout &DL = + if (jst->layout) { + const DataLayout &DL = #if JL_LLVM_VERSION >= 30600 - jl_ExecutionEngine->getDataLayout(); + jl_ExecutionEngine->getDataLayout(); #else - *jl_ExecutionEngine->getDataLayout(); + *jl_ExecutionEngine->getDataLayout(); #endif - unsigned llvm_alignment = DL.getABITypeAlignment((Type*)jst->struct_decl); - unsigned julia_alignment = jst->layout->alignment; - assert(llvm_alignment == julia_alignment); + unsigned llvm_alignment = DL.getABITypeAlignment((Type*)jst->struct_decl); + unsigned julia_alignment = jst->layout->alignment; + assert(llvm_alignment == julia_alignment); + } #endif } return (Type*)jst->struct_decl; } - return julia_type_to_llvm(jt, isboxed); + if (isboxed) *isboxed = true; + return T_pjlvalue; } static bool is_datatype_all_pointers(jl_datatype_t *dt) @@ -750,6 +789,17 @@ static void emit_typecheck(const jl_cgval_t &x, jl_value_t *type, const std::str builder.SetInsertPoint(passBB); } +static void emit_leafcheck(Value *typ, const std::string &msg, jl_codectx_t *ctx) +{ + assert(typ->getType() == T_pjlvalue); + emit_typecheck(mark_julia_type(typ, true, jl_any_type, ctx, false), (jl_value_t*)jl_datatype_type, msg, ctx); + Value *isleaf; + isleaf = builder.CreateConstInBoundsGEP1_32(LLVM37_param(T_int8) emit_bitcast(typ, T_pint8), offsetof(jl_datatype_t, isleaftype)); + isleaf = builder.CreateLoad(isleaf, tbaa_const); + isleaf = builder.CreateTrunc(isleaf, T_int1); + error_unless(isleaf, msg, ctx); +} + #define CHECK_BOUNDS 1 static Value *emit_bounds_check(const jl_cgval_t &ainfo, jl_value_t *ty, Value *i, Value *len, jl_codectx_t *ctx) { @@ -832,7 +882,6 @@ static jl_cgval_t typed_load(Value *ptr, Value *idx_0based, jl_value_t *jltype, { bool isboxed; Type *elty = julia_type_to_llvm(jltype, &isboxed); - assert(elty != NULL); if (type_is_ghost(elty)) return ghostValue(jltype); Value *data; @@ -870,7 +919,6 @@ static void typed_store(Value *ptr, Value *idx_0based, const jl_cgval_t &rhs, { bool isboxed; Type *elty = julia_type_to_llvm(jltype, &isboxed); - assert(elty != NULL); if (type_is_ghost(elty)) return; Value *r; @@ -1051,7 +1099,6 @@ static jl_cgval_t emit_getfield_knownidx(const jl_cgval_t &strct, unsigned idx, { jl_value_t *jfty = jl_field_type(jt, idx); Type *elty = julia_type_to_llvm(jfty); - assert(elty != NULL); if (jfty == jl_bottom_type) { raise_exception(literal_pointer_val(jl_undefref_exception), ctx); return jl_cgval_t(); // unreachable @@ -1335,10 +1382,10 @@ static Value *emit_array_nd_index(const jl_cgval_t &ainfo, jl_value_t *ex, size_ static Value *emit_allocobj(jl_codectx_t *ctx, size_t static_size, Value *jt); static Value *emit_allocobj(jl_codectx_t *ctx, size_t static_size, const jl_cgval_t &v); -static Value *init_bits_value(Value *newv, Value *v, MDNode *tbaa) +static Value *init_bits_value(Value *newv, Value *v, MDNode *tbaa, unsigned alignment = sizeof(void*)) // min alignment in julia's gc is pointer-aligned { // newv should already be tagged - tbaa_decorate(tbaa, builder.CreateAlignedStore(v, emit_bitcast(newv, PointerType::get(v->getType(),0)), sizeof(void*))); // min alignment in julia's gc is pointer-aligned + tbaa_decorate(tbaa, builder.CreateAlignedStore(v, emit_bitcast(newv, PointerType::get(v->getType(),0)), alignment)); return newv; } static Value *as_value(Type *t, const jl_cgval_t&); @@ -1532,7 +1579,7 @@ static Value *boxed(const jl_cgval_t &vinfo, jl_codectx_t *ctx, bool gcrooted) static void emit_cpointercheck(const jl_cgval_t &x, const std::string &msg, jl_codectx_t *ctx) { Value *t = emit_typeof_boxed(x,ctx); - emit_typecheck(mark_julia_type(t, true, jl_any_type, ctx), (jl_value_t*)jl_datatype_type, msg, ctx); + emit_typecheck(mark_julia_type(t, true, jl_any_type, ctx, false), (jl_value_t*)jl_datatype_type, msg, ctx); Value *istype = builder.CreateICmpEQ(emit_datatype_name(t), diff --git a/src/codegen.cpp b/src/codegen.cpp index fe5fca2cc3785..f4c49ba310a29 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -357,6 +357,7 @@ static Function *jlegal_func; static Function *jlalloc_pool_func; static Function *jlalloc_big_func; static Function *jlsubtype_func; +static Function *jlapplytype_func; static Function *setjmp_func; static Function *memcmp_func; static Function *box_int8_func; @@ -566,6 +567,7 @@ static Value *make_jlcall(ArrayRef args, jl_codectx_t *ctx); static Value *global_binding_pointer(jl_module_t *m, jl_sym_t *s, jl_binding_t **pbnd, bool assign, jl_codectx_t *ctx); static jl_cgval_t emit_checked_var(Value *bp, jl_sym_t *name, jl_codectx_t *ctx, bool isvol, MDNode *tbaa); +static jl_cgval_t emit_sparam(size_t i, jl_codectx_t *ctx); static Value *emit_condition(const jl_cgval_t &condV, const std::string &msg, jl_codectx_t *ctx); static void allocate_gc_frame(BasicBlock *b0, jl_codectx_t *ctx); static GlobalVariable *prepare_global(GlobalVariable *G, Module *M = jl_builderModule); @@ -1047,33 +1049,7 @@ static jl_method_instance_t *jl_get_unspecialized(jl_method_instance_t *method) { // one unspecialized version of a function can be shared among all cached specializations jl_method_t *def = method->def; - if (def->needs_sparam_vals_ducttape == 2) { - if (def->isstaged) { - def->needs_sparam_vals_ducttape = 1; - } - else { - // determine if this needs an unspec version compiled for each - // sparam, or whether they can be shared - // TODO: remove this once runtime intrinsics are hooked up - int needs_sparam_vals_ducttape = 0; - if (method->sparam_vals != jl_emptysvec) { - jl_array_t *code = (jl_array_t*)def->source->code; - JL_GC_PUSH1(&code); - if (!jl_typeis(code, jl_array_any_type)) - code = jl_uncompress_ast(def, code); - size_t i, l = jl_array_len(code); - for (i = 0; i < l; i++) { - if (jl_has_intrinsics(method, jl_array_ptr_ref(code, i), def->module)) { - needs_sparam_vals_ducttape = 1; - break; - } - } - JL_GC_POP(); - } - def->needs_sparam_vals_ducttape = needs_sparam_vals_ducttape; - } - } - if (def->needs_sparam_vals_ducttape) { + if (def->isstaged) { return method; } if (def->unspecialized == NULL) { @@ -2070,7 +2046,8 @@ static Value *make_jlcall(ArrayRef args, jl_codectx_t *ctx) static void jl_add_method_root(jl_codectx_t *ctx, jl_value_t *val) { - if (jl_is_leaf_type(val) || jl_is_bool(val) || jl_is_symbol(val)) + if (jl_is_leaf_type(val) || jl_is_bool(val) || jl_is_symbol(val) || + val == (jl_value_t*)jl_any_type || val == (jl_value_t*)jl_bottom_type) return; JL_GC_PUSH1(&val); if (ctx->roots == NULL) { @@ -3080,7 +3057,7 @@ static jl_cgval_t emit_sparam(size_t i, jl_codectx_t *ctx) Value *bp = builder.CreateConstInBoundsGEP1_32(LLVM37_param(T_pjlvalue) emit_bitcast(ctx->spvals_ptr, T_ppjlvalue), i + sizeof(jl_svec_t) / sizeof(jl_value_t*)); - return mark_julia_type(tbaa_decorate(tbaa_const, builder.CreateLoad(bp)), true, jl_any_type, ctx); + return mark_julia_type(tbaa_decorate(tbaa_const, builder.CreateLoad(bp)), true, jl_any_type, ctx, false); } static jl_cgval_t emit_global(jl_sym_t *sym, jl_codectx_t *ctx) @@ -3551,7 +3528,7 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t jl_error("cfunction: return type doesn't correspond to a C type"); size_t nargs = jl_nparams(argt); - function_sig_t sig(crt, jlrettype, argt->parameters, nargs, false, CallingConv::C, false); + function_sig_t sig(crt, jlrettype, toboxed, argt->parameters, jl_emptysvec, nargs, false, CallingConv::C, false); if (!sig.err_msg.empty()) jl_error(sig.err_msg.c_str()); if (sig.fargt.size() + sig.sret != sig.fargt_sig.size()) @@ -3716,8 +3693,14 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t else { // something of type T // undo whatever we might have done to this poor argument - bool issigned = jl_signed_type && jl_subtype(jargty, (jl_value_t*)jl_signed_type, 0); - val = llvm_type_rewrite(val, val->getType(), sig.fargt[i], true, sig.byRefList[i], issigned, &ctx); + if (sig.byRefList.at(i)) { + assert(val->getType() == sig.fargt[i]->getPointerTo()); + val = builder.CreateAlignedLoad(val, 1); // unknown alignment from C + } + else { + bool issigned = jl_signed_type && jl_subtype(jargty, (jl_value_t*)jl_signed_type, 0); + val = llvm_type_rewrite(val, sig.fargt[i], issigned, &ctx); + } bool isboxed; (void)julia_type_to_llvm(jargty, &isboxed); if (isboxed) { @@ -3831,8 +3814,8 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t prt = sig.fargt_sig[0]->getContainedType(0); // sret is a PointerType bool issigned = jl_signed_type && jl_subtype(declrt, (jl_value_t*)jl_signed_type, 0); Value *v = julia_to_native(sig.lrt, toboxed, declrt, retval, - false, false, false, false, 0, &ctx, NULL); - r = llvm_type_rewrite(v, sig.lrt, prt, false, false, issigned, &ctx); + false, 0, &ctx, NULL); + r = llvm_type_rewrite(v, prt, issigned, &ctx); if (sig.sret) builder.CreateStore(r, sretPtr); } @@ -5739,6 +5722,16 @@ static void init_julia_llvm_env(Module *m) "jl_subtype", m); add_named_global(jlsubtype_func, &jl_subtype); + std::vector applytype_args(0); + applytype_args.push_back(T_pjlvalue); + applytype_args.push_back(T_pjlvalue); + applytype_args.push_back(T_ppjlvalue); + jlapplytype_func = + Function::Create(FunctionType::get(T_pjlvalue, applytype_args, false), + Function::ExternalLinkage, + "jl_instantiate_type_in_env", m); + add_named_global(jlapplytype_func, &jl_instantiate_type_in_env); + std::vector alloc_pool_args(0); alloc_pool_args.push_back(T_pint8); alloc_pool_args.push_back(T_int32); diff --git a/src/dump.c b/src/dump.c index 3d10b5061a02f..a6220763558a1 100644 --- a/src/dump.c +++ b/src/dump.c @@ -902,7 +902,6 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v) jl_serialize_value(s, (jl_value_t*)m->source); jl_serialize_value(s, (jl_value_t*)m->unspecialized); jl_serialize_value(s, (jl_value_t*)m->invokes.unknown); - write_int8(s->s, m->needs_sparam_vals_ducttape); } else if (jl_is_method_instance(v)) { writetag(s->s, jl_method_instance_type); @@ -1531,7 +1530,6 @@ static jl_value_t *jl_deserialize_value_method(jl_serializer_state *s) jl_gc_wb(m, m->unspecialized); m->invokes.unknown = jl_deserialize_value(s, (jl_value_t**)&m->invokes); jl_gc_wb(m, m->invokes.unknown); - m->needs_sparam_vals_ducttape = read_int8(s->s); m->traced = 0; JL_MUTEX_INIT(&m->writelock); return (jl_value_t*)m; diff --git a/src/gf.c b/src/gf.c index 6de0a406caa34..b83180aaa33ec 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1243,7 +1243,7 @@ jl_llvm_functions_t jl_compile_for_dispatch(jl_method_instance_t *li) if (jl_options.compile_enabled == JL_OPTIONS_COMPILE_OFF) { jl_printf(JL_STDERR, "code missing for "); jl_static_show(JL_STDERR, (jl_value_t*)li); - jl_printf(JL_STDERR, " sysimg may not have been built with --compile=all\n"); + jl_printf(JL_STDERR, " : sysimg may not have been built with --compile=all\n"); } } jl_llvm_functions_t decls = li->functionObjectsDecls; @@ -1641,7 +1641,7 @@ static void _precompile_enq_module(jl_module_t *m, jl_array_t *unspec) // removes all method caches size_t i; void **table = m->bindings.table; - for(i=1; i < m->bindings.size; i+=2) { + for (i = 1; i < m->bindings.size; i += 2) { if (table[i] != HT_NOTFOUND) { jl_binding_t *b = (jl_binding_t*)table[i]; if (b->owner == m && b->value && b->constp) { diff --git a/src/interpreter.c b/src/interpreter.c index 93ffdff932f72..509195298e07c 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -228,9 +228,7 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) ssize_t n = jl_unbox_long(args[0]); assert(n > 0); if (s->sparam_vals && n <= jl_svec_len(s->sparam_vals)) { - jl_value_t *sp = jl_svecref(s->sparam_vals, n - 1); - if (!jl_is_typevar(sp)) - return sp; + return jl_svecref(s->sparam_vals, n - 1); } // static parameter val unknown needs to be an error for ccall jl_error("could not determine static parameter value"); diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index b92112b936909..989fcc1eb8ad7 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -403,21 +403,6 @@ static jl_value_t *staticeval_bitstype(const jl_cgval_t &targ) return NULL; } -static Type *bitstype_to_llvm(jl_value_t *bt) -{ - assert(jl_is_bitstype(bt)); - bool isboxed; - Type *to = julia_type_to_llvm(bt, &isboxed); - assert(!type_is_ghost(to)); - if (to == NULL || isboxed) { - // might be some sort of incomplete (but valid) Ptr{T} type, for example - unsigned int nb = jl_datatype_nbits(bt); - to = IntegerType::get(jl_LLVMContext, nb); - } - assert(!to->isAggregateType()); // expecting a bits type - return to; // IntegerType, FloatingPointType, or PointerType -} - static jl_cgval_t emit_runtime_call(JL_I::intrinsic f, const jl_cgval_t *argv, size_t nargs, jl_codectx_t *ctx) { Value *func = prepare_call(runtime_func[f]); diff --git a/src/jltypes.c b/src/jltypes.c index 19dd96508e374..07b7e94114dc7 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -141,7 +141,7 @@ JL_DLLEXPORT int jl_has_typevars_(jl_value_t *v, int incl_wildcard) return jl_has_typevars__(v, incl_wildcard, NULL, 0); } -static int jl_has_typevars_from(jl_value_t *v, jl_svec_t *p) +int jl_has_typevars_from(jl_value_t *v, jl_svec_t *p) { if (jl_svec_len(p) == 0) return 0; return jl_has_typevars__(v, 0, jl_svec_data(p), jl_svec_len(p)); @@ -2544,6 +2544,32 @@ jl_value_t *jl_instantiate_type_with(jl_value_t *t, jl_value_t **env, size_t n) return inst_type_w_((jl_value_t*)t, env, n, NULL, 1); } +JL_DLLEXPORT jl_value_t *jl_instantiate_type_in_env(jl_value_t *ty, jl_value_t *sparam_syms, jl_value_t **sparam_vals) +{ + jl_value_t *typ; + JL_TRY { + if (jl_is_typevar(sparam_syms)) { + jl_value_t *env1[2]; + env1[0] = sparam_syms; + env1[1] = sparam_vals[0]; + typ = jl_instantiate_type_with(ty, env1, 1); + } + else { + size_t i, np = jl_svec_len(sparam_syms); + jl_value_t **env = (jl_value_t**)alloca(sizeof(jl_value_t*) * 2 * np); + for (i = 0; i < np; i++) { + env[i * 2 + 0] = jl_svecref(sparam_syms, i); + env[i * 2 + 1] = sparam_vals[i]; + } + typ = jl_instantiate_type_with(ty, env, np); + } + } + JL_CATCH { + typ = jl_bottom_type; + } + return typ; +} + jl_datatype_t *jl_wrap_Type(jl_value_t *t) { jl_value_t *env[2]; diff --git a/src/julia.h b/src/julia.h index 04f1ab4ab3ab6..8a8d614a81eb7 100644 --- a/src/julia.h +++ b/src/julia.h @@ -255,10 +255,6 @@ typedef struct _jl_method_t { int32_t called; // bit flags: whether each of the first 8 arguments is called uint8_t isva; uint8_t isstaged; - // if there are intrinsic calls, sparams are probably required to compile successfully, - // and so unspecialized will be created for each linfo instead of using linfo->def->template - // 0 = no, 1 = yes, 2 = not yet known - uint8_t needs_sparam_vals_ducttape; // hidden fields: uint8_t traced; diff --git a/src/julia_internal.h b/src/julia_internal.h index e42ffcad28407..7e9c600392b05 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -332,6 +332,8 @@ jl_value_t *jl_type_intersection_matching(jl_value_t *a, jl_value_t *b, jl_svec_t **penv, jl_svec_t *tvars); jl_value_t *jl_apply_type_(jl_value_t *tc, jl_value_t **params, size_t n); jl_value_t *jl_instantiate_type_with(jl_value_t *t, jl_value_t **env, size_t n); +JL_DLLEXPORT jl_value_t *jl_instantiate_type_in_env(jl_value_t *ty, jl_value_t *sparam_syms, jl_value_t **sparam_vals); +int jl_has_typevars_from(jl_value_t *v, jl_svec_t *p); jl_datatype_t *jl_new_uninitialized_datatype(void); jl_datatype_t *jl_new_abstracttype(jl_value_t *name, jl_datatype_t *super, jl_svec_t *parameters); @@ -364,7 +366,6 @@ jl_method_instance_t *jl_method_lookup(jl_methtable_t *mt, jl_value_t **args, si jl_value_t *jl_gf_invoke(jl_tupletype_t *types, jl_value_t **args, size_t nargs); jl_datatype_t *jl_first_argument_datatype(jl_value_t *argtypes); -int jl_has_intrinsics(jl_method_instance_t *li, jl_value_t *v, jl_module_t *m); jl_value_t *jl_nth_slot_type(jl_tupletype_t *sig, size_t i); void jl_compute_field_offsets(jl_datatype_t *st); diff --git a/src/toplevel.c b/src/toplevel.c index 976bfdeab5c39..da8161bf54e2e 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -508,11 +508,17 @@ int jl_is_toplevel_only_expr(jl_value_t *e) ((jl_expr_t*)e)->head == toplevel_sym); } -static jl_method_instance_t *jl_new_thunk(jl_code_info_t *src) +jl_value_t *jl_resolve_globals(jl_value_t *expr, jl_module_t *module, jl_svec_t *sparam_vals); +static jl_method_instance_t *jl_new_thunk(jl_code_info_t *src, jl_module_t *module) { jl_method_instance_t *li = jl_new_method_instance_uninit(); li->inferred = (jl_value_t*)src; li->specTypes = (jl_tupletype_t*)jl_typeof(jl_emptytuple); + jl_array_t *stmts = (jl_array_t*)src->code; + size_t i, l; + for (i = 0, l = jl_array_len(stmts); i < l; i++) { + jl_array_ptr_set(stmts, i, jl_resolve_globals(jl_array_ptr_ref(stmts, i), module, NULL)); + } return li; } @@ -636,7 +642,7 @@ jl_value_t *jl_toplevel_eval_flex(jl_value_t *e, int fast, int expanded) } if (ewc) { - li = jl_new_thunk(thk); + li = jl_new_thunk(thk, ptls->current_module); jl_type_infer(li, 0); jl_value_t *dummy_f_arg = NULL; result = jl_call_method_internal(li, &dummy_f_arg, 1); diff --git a/test/ccall.jl b/test/ccall.jl index 3fb2ecf54edad..77ee04fae5a8e 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -15,7 +15,7 @@ ccall_test_func(x) = ccall((:testUcharX, libccalltest), Int32, (UInt8,), x % UIn # Test for proper round-trip of Ref{T} type -ccall_echo_func{T,U}(x, ::Type{T}, ::Type{U}) = ccall((:test_echo_p, libccalltest), T, (U,), x) +ccall_echo_func(x::ANY, T::Type, U::Type) = (@eval (x) -> ccall((:test_echo_p, libccalltest), $T, ($U,), x))(x) # Make sure object x is still valid (rooted as argument) # when loading the pointer. This works as long as we still keep the argument # rooted but might fail if we are smarter about eliminating dead root. @@ -63,7 +63,7 @@ let a, ci_ary, x x = ccall((:cptest_static, libccalltest), Ptr{Complex{Int}}, (Ptr{Complex{Int}},), &a) @test unsafe_load(x) == a - Libc.free(convert(Ptr{Void},x)) + Libc.free(convert(Ptr{Void}, x)) end let a, b, x @@ -123,7 +123,11 @@ function test_struct1{Struct}(::Type{Struct}) b = Float32(123.456) a2 = copy(a) - x = ccall((:test_1, libccalltest), Struct, (Struct, Float32), a2, b) + if Struct === Struct1 + x = ccall((:test_1, libccalltest), Struct1, (Struct1, Float32), a2, b) + else + x = ccall((:test_1, libccalltest), Struct1I, (Struct1I, Float32), a2, b) + end @test a2.x == a.x && a2.y == a.y @test !(a2 === x) @@ -178,7 +182,11 @@ function test_struct4{Struct}(::Type{Struct}) a = Struct(-512275808,882558299,-2133022131) b = Int32(42) - x = ccall((:test_4, libccalltest), Struct, (Struct, Int32), a, b) + if Struct === Struct4 + x = ccall((:test_4, libccalltest), Struct4, (Struct4, Int32), a, b) + else + x = ccall((:test_4, libccalltest), Struct4I, (Struct4I, Int32), a, b) + end @test x.x == a.x+b*1 @test x.y == a.y-b*2 @@ -204,7 +212,11 @@ function test_struct5{Struct}(::Type{Struct}) a = Struct(1771319039, 406394736, -1269509787, -745020976) b = Int32(42) - x = ccall((:test_5, libccalltest), Struct, (Struct, Int32), a, b) + if Struct === Struct5 + x = ccall((:test_5, libccalltest), Struct5, (Struct5, Int32), a, b) + else + x = ccall((:test_5, libccalltest), Struct5I, (Struct5I, Int32), a, b) + end @test x.x == a.x+b*1 @test x.y == a.y-b*2 @@ -229,7 +241,11 @@ function test_struct6{Struct}(::Type{Struct}) a = Struct(-654017936452753226, -5573248801240918230, -983717165097205098) b = Int64(42) - x = ccall((:test_6, libccalltest), Struct, (Struct, Int64), a, b) + if Struct === Struct6 + x = ccall((:test_6, libccalltest), Struct6, (Struct6, Int64), a, b) + else + x = ccall((:test_6, libccalltest), Struct6I, (Struct6I, Int64), a, b) + end @test x.x == a.x+b*1 @test x.y == a.y-b*2 @@ -251,7 +267,11 @@ function test_struct7{Struct}(::Type{Struct}) a = Struct(-384082741977533896, 'h') b = Int8(42) - x = ccall((:test_7, libccalltest), Struct, (Struct, Int8), a, b) + if Struct === Struct7 + x = ccall((:test_7, libccalltest), Struct7, (Struct7, Int8), a, b) + else + x = ccall((:test_7, libccalltest), Struct7I, (Struct7I, Int8), a, b) + end @test x.x == a.x+Int(b)*1 @test x.y == a.y-Int(b)*2 @@ -272,7 +292,11 @@ function test_struct8{Struct}(::Type{Struct}) a = Struct(-384082896, 'h') b = Int8(42) - r8 = ccall((:test_8, libccalltest), Struct, (Struct, Int8), a, b) + if Struct === Struct8 + r8 = ccall((:test_8, libccalltest), Struct8, (Struct8, Int8), a, b) + else + r8 = ccall((:test_8, libccalltest), Struct8I, (Struct8I, Int8), a, b) + end @test r8.x == a.x+b*1 @test r8.y == a.y-b*2 @@ -293,7 +317,11 @@ function test_struct9{Struct}(::Type{Struct}) a = Struct(-394092996, -3840) b = Int16(42) - x = ccall((:test_9, libccalltest), Struct, (Struct, Int16), a, b) + if Struct === Struct9 + x = ccall((:test_9, libccalltest), Struct9, (Struct9, Int16), a, b) + else + x = ccall((:test_9, libccalltest), Struct9I, (Struct9I, Int16), a, b) + end @test x.x == a.x+b*1 @test x.y == a.y-b*2 @@ -318,7 +346,11 @@ function test_struct10{Struct}(::Type{Struct}) a = Struct('0', '1', '2', '3') b = Int8(2) - x = ccall((:test_10, libccalltest), Struct, (Struct, Int8), a, b) + if Struct === Struct10 + x = ccall((:test_10, libccalltest), Struct10, (Struct10, Int8), a, b) + else + x = ccall((:test_10, libccalltest), Struct10I, (Struct10I, Int8), a, b) + end @test x.x == a.x+b*1 @test x.y == a.y-b*2 @@ -339,7 +371,11 @@ function test_struct11{Struct}(::Type{Struct}) a = Struct(0.8877077f0 + 0.4591081f0im) b = Float32(42) - x = ccall((:test_11, libccalltest), Struct, (Struct, Float32), a, b) + if Struct === Struct11 + x = ccall((:test_11, libccalltest), Struct11, (Struct11, Float32), a, b) + else + x = ccall((:test_11, libccalltest), Struct11I, (Struct11I, Float32), a, b) + end @test x.x ≈ a.x + b*1 - b*2im end @@ -359,7 +395,11 @@ function test_struct12{Struct}(::Type{Struct}) a = Struct(0.8877077f5 + 0.4591081f2im, 0.0004842868f0 - 6982.3265f3im) b = Float32(42) - x = ccall((:test_12, libccalltest), Struct, (Struct, Float32), a, b) + if Struct === Struct12 + x = ccall((:test_12, libccalltest), Struct12, (Struct12, Float32), a, b) + else + x = ccall((:test_12, libccalltest), Struct12I, (Struct12I, Float32), a, b) + end @test x.x ≈ a.x + b*1 - b*2im @test x.y ≈ a.y + b*3 - b*4im @@ -378,7 +418,11 @@ function test_struct13{Struct}(::Type{Struct}) a = Struct(42968.97560380495 - 803.0576845153616im) b = Float64(42) - x = ccall((:test_13, libccalltest), Struct, (Struct, Float64), a, b) + if Struct === Struct13 + x = ccall((:test_13, libccalltest), Struct13, (Struct13, Float64), a, b) + else + x = ccall((:test_13, libccalltest), Struct13I, (Struct13I, Float64), a, b) + end @test x.x ≈ a.x + b*1 - b*2im end @@ -398,7 +442,11 @@ function test_struct14{Struct}(::Type{Struct}) a = Struct(0.024138331f0, 0.89759064f32) b = Float32(42) - x = ccall((:test_14, libccalltest), Struct, (Struct, Float32), a, b) + if Struct === Struct14 + x = ccall((:test_14, libccalltest), Struct14, (Struct14, Float32), a, b) + else + x = ccall((:test_14, libccalltest), Struct14I, (Struct14I, Float32), a, b) + end @test x.x ≈ a.x + b*1 @test x.y ≈ a.y - b*2 @@ -419,7 +467,11 @@ function test_struct15{Struct}(::Type{Struct}) a = Struct(4.180997967273657, -0.404218594294923) b = Float64(42) - x = ccall((:test_15, libccalltest), Struct, (Struct, Float64), a, b) + if Struct === Struct15 + x = ccall((:test_15, libccalltest), Struct15, (Struct15, Float64), a, b) + else + x = ccall((:test_15, libccalltest), Struct15I, (Struct15I, Float64), a, b) + end @test x.x ≈ a.x + b*1 @test x.y ≈ a.y - b*2 @@ -449,7 +501,11 @@ function test_struct16{Struct}(::Type{Struct}) 0.6460273620993535, 0.9472692581106656, 0.47328535437352093) b = Float32(42) - x = ccall((:test_16, libccalltest), Struct, (Struct, Float32), a, b) + if Struct === Struct16 + x = ccall((:test_16, libccalltest), Struct16, (Struct16, Float32), a, b) + else + x = ccall((:test_16, libccalltest), Struct16I, (Struct16I, Float32), a, b) + end @test x.x ≈ a.x + b*1 @test x.y ≈ a.y - b*2 @@ -474,7 +530,11 @@ function test_struct17{Struct}(::Type{Struct}) a = Struct(2, 10) b = Int8(2) - x = ccall((:test_17, libccalltest), Struct, (Struct, Int8), a, b) + if Struct === Struct17 + x = ccall((:test_17, libccalltest), Struct17, (Struct17, Int8), a, b) + else + x = ccall((:test_17, libccalltest), Struct17I, (Struct17I, Int8), a, b) + end @test x.a == a.a + b * 1 @test x.b == a.b - b * 2 @@ -497,7 +557,11 @@ function test_struct18{Struct}(::Type{Struct}) a = Struct(2, 10, -3) b = Int8(2) - x = ccall((:test_18, libccalltest), Struct, (Struct, Int8), a, b) + if Struct === Struct18 + x = ccall((:test_18, libccalltest), Struct18, (Struct18, Int8), a, b) + else + x = ccall((:test_18, libccalltest), Struct18I, (Struct18I, Int8), a, b) + end @test x.a == a.a + b * 1 @test x.b == a.b - b * 2 @@ -533,7 +597,11 @@ function test_struct_big{Struct}(::Type{Struct}) a = Struct(424,-5,Int8('Z')) a2 = copy(a) - x = ccall((:test_big, libccalltest), Struct, (Struct,), a2) + if Struct == Struct_Big + x = ccall((:test_big, libccalltest), Struct_Big, (Struct_Big,), a2) + else + x = ccall((:test_big, libccalltest), Struct_BigI, (Struct_BigI,), a2) + end @test a2.x == a.x && a2.y == a.y && a2.z == a.z @test x.x == a.x + 1 @@ -851,28 +919,35 @@ type Struct_huge5_ppc64_hva end if Sys.ARCH === :x86_64 - function test_sse(a1::V4xF32,a2::V4xF32,a3::V4xF32,a4::V4xF32) - ccall((:test_m128, libccalltest), V4xF32, (V4xF32,V4xF32,V4xF32,V4xF32), a1, a2, a3, a4) + function test_sse(a1::V4xF32, a2::V4xF32, a3::V4xF32, a4::V4xF32) + ccall((:test_m128, libccalltest), V4xF32, (V4xF32, V4xF32, V4xF32, V4xF32), a1, a2, a3, a4) end - function test_sse(a1::V4xI32,a2::V4xI32,a3::V4xI32,a4::V4xI32) - ccall((:test_m128i, libccalltest), V4xI32, (V4xI32,V4xI32,V4xI32,V4xI32), a1, a2, a3, a4) + function test_sse(a1::V4xI32, a2::V4xI32, a3::V4xI32, a4::V4xI32) + ccall((:test_m128i, libccalltest), V4xI32, (V4xI32, V4xI32, V4xI32, V4xI32), a1, a2, a3, a4) end - foo_ams(a1, a2, a3, a4) = VecReg(ntuple(i->VecElement(a1[i].value+a2[i].value*(a3[i].value-a4[i].value)),4)) + foo_ams(a1, a2, a3, a4) = VecReg(ntuple(i -> VecElement(a1[i].value + a2[i].value * (a3[i].value - a4[i].value)), 4)) - rt_sse{T}(a1::T,a2::T,a3::T,a4::T) = ccall(cfunction(foo_ams,T,(T,T,T,T)), T, (T,T,T,T), a1, a2, a3,a4) + for s in [Float32, Int32] + T = NTuple{4, VecElement{s}} + @eval function rt_sse(a1::$T, a2::$T, a3::$T, a4::$T) + return ccall( + cfunction(foo_ams, $T, ($T, $T, $T, $T)), + $T, + ($T, $T, $T, $T), + a1, a2, a3, a4) + end - for s in [Float32,Int32] - a1 = VecReg(ntuple(i->VecElement(s(1i)),4)) - a2 = VecReg(ntuple(i->VecElement(s(2i)),4)) - a3 = VecReg(ntuple(i->VecElement(s(3i)),4)) - a4 = VecReg(ntuple(i->VecElement(s(4i)),4)) - r = VecReg(ntuple(i->VecElement(s(1i+2i*(3i-4i))),4)) - @test test_sse(a1,a2,a3,a4) == r + a1 = VecReg(ntuple(i -> VecElement(s(1i)), 4)) + a2 = VecReg(ntuple(i -> VecElement(s(2i)), 4)) + a3 = VecReg(ntuple(i -> VecElement(s(3i)), 4)) + a4 = VecReg(ntuple(i -> VecElement(s(4i)), 4)) + r = VecReg(ntuple(i -> VecElement(s(1i + 2i * (3i - 4i))), 4)) + @test test_sse(a1, a2, a3, a4) == r # cfunction round-trip - @test rt_sse(a1,a2,a3,a4) == r + @test rt_sse(a1, a2, a3, a4) == r end elseif Sys.ARCH === :aarch64 diff --git a/test/compile.jl b/test/compile.jl index 991026d9a5aa6..d71c1138227c9 100644 --- a/test/compile.jl +++ b/test/compile.jl @@ -103,7 +103,7 @@ try let some_method = @which Base.include("string") # global const some_method // FIXME: support for serializing a direct reference to an external Method not implemented global const some_linfo = - ccall(:jl_specializations_get_linfo, Ref{MethodInstance}, (Any, Any, Any), + ccall(:jl_specializations_get_linfo, Ref{Core.MethodInstance}, (Any, Any, Any), some_method, Tuple{typeof(Base.include), String}, Core.svec()) end end @@ -169,7 +169,7 @@ try 0:25) some_method = @which Base.include("string") some_linfo = - ccall(:jl_specializations_get_linfo, Ref{MethodInstance}, (Any, Any, Any), + ccall(:jl_specializations_get_linfo, Ref{Core.MethodInstance}, (Any, Any, Any), some_method, Tuple{typeof(Base.include), String}, Core.svec()) @test Foo.some_linfo::Core.MethodInstance === some_linfo