diff --git a/.gitignore b/.gitignore index d3126887d0b12..ee3c038c0c5db 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ *.do *.o *.obj +*.so *.dylib *.dSYM *.jl.cov diff --git a/base/abstractarray.jl b/base/abstractarray.jl index becbe491695d9..b130367e3d8e8 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -47,6 +47,17 @@ else end end +# convert Arrays to pointer arrays for ccall +cconvert_gcroot{P<:Ptr}(::Type{Ptr{P}}, a::Array{P}) = a +function cconvert_gcroot{P<:Ptr}(::Type{Ptr{P}}, a::Array) + ptrs = Array(P, length(a)+1) + for i = 1:length(a) + ptrs[i] = convert(P, a[i]) + end + ptrs[length(a)+1] = C_NULL + return ptrs +end + size{T,n}(t::AbstractArray{T,n}, d) = d <= n ? size(t)[d] : 1 size(x, d1::Integer, d2::Integer, dx::Integer...) = tuple(size(x, d1), size(x, d2, dx...)...) eltype{T}(::Type{AbstractArray{T}}) = T diff --git a/base/base.jl b/base/base.jl index eecfcfdcc7399..0aab4f545d362 100644 --- a/base/base.jl +++ b/base/base.jl @@ -52,16 +52,12 @@ convert{T}(::Type{(T...)}, x::Tuple) = cnvt_all(T, x...) cnvt_all(T) = () cnvt_all(T, x, rest...) = tuple(convert(T,x), cnvt_all(T, rest...)...) - -ptr_arg_convert{T}(::Type{Ptr{T}}, x) = convert(T, x) -ptr_arg_convert(::Type{Ptr{Void}}, x) = x - -# conversion used by ccall -cconvert(T, x) = convert(T, x) -# use the code in ccall.cpp to safely allocate temporary pointer arrays -cconvert{T}(::Type{Ptr{Ptr{T}}}, a::Array) = a -# convert strings to ByteString to pass as pointers -cconvert{P<:Union(Int8,UInt8)}(::Type{Ptr{P}}, s::AbstractString) = bytestring(s) +# conversions used by ccall +ptr_arg_cconvert_gcroot{T}(::Type{Ptr{T}}, x) = cconvert_gcroot(T, x) +ptr_arg_cconvert{T}(::Type{Ptr{T}}, x) = cconvert(T, x) +ptr_arg_cconvert(::Type{Ptr{Void}}, x) = x +cconvert_gcroot(T, x) = x +cconvert(T, x) = convert(T,x) reinterpret{T,S}(::Type{T}, x::S) = box(T,unbox(S,x)) diff --git a/base/boot.jl b/base/boot.jl index 686aea13b16f6..019c045811777 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -73,7 +73,8 @@ # contents::T #end -#bitstype {32|64} Ptr{T} +#abstract Ref{T} +#bitstype {32|64} Ptr{T} <: Ref{T} # types for the front end @@ -125,7 +126,7 @@ export Module, Symbol, Task, Array, GenSym, # numeric types Bool, FloatingPoint, Float16, Float32, Float64, Number, Integer, Int, Int8, Int16, - Int32, Int64, Int128, Ptr, Real, Signed, UInt, UInt8, UInt16, UInt32, + Int32, Int64, Int128, Ref, Ptr, Real, Signed, UInt, UInt8, UInt16, UInt32, UInt64, UInt128, Unsigned, # string types Char, ASCIIString, ByteString, DirectIndexString, AbstractString, UTF8String, diff --git a/base/inference.jl b/base/inference.jl index 35e8add58da5d..2509413eee054 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -112,7 +112,20 @@ t_func[fpiseq] = (2, 2, cmp_tfunc) t_func[fpislt] = (2, 2, cmp_tfunc) t_func[nan_dom_err] = (2, 2, (a, b)->a) t_func[eval(Core.Intrinsics,:ccall)] = - (3, Inf, (fptr, rt, at, a...)->(isType(rt) ? rt.parameters[1] : Any)) + (3, Inf, function(fptr, rt, at, a...) + if !isType(rt) + return Any + end + t = rt.parameters[1] + if isa(t,DataType) && is((t::DataType).name,Ref.name) + t = t.parameters[1] + if is(t,Any) + return Union() # a return type of Box{Any} is invalid + end + return t + end + return t + end) t_func[eval(Core.Intrinsics,:llvmcall)] = (3, Inf, (fptr, rt, at, a...)->(isType(rt) ? rt.parameters[1] : isa(rt,Tuple) ? map(x->x.parameters[1],rt) : Any)) diff --git a/base/pcre.jl b/base/pcre.jl index 55935b98e895e..30abd8f148a08 100644 --- a/base/pcre.jl +++ b/base/pcre.jl @@ -57,7 +57,7 @@ function info{T}( regex::Ptr{Void}, extra::Ptr{Void}, what::Integer, ::Type{T} ) - buf = Array(UInt8,sizeof(T)) + buf = zeros(UInt8,sizeof(T)) ret = ccall((:pcre_fullinfo, :libpcre), Int32, (Ptr{Void}, Ptr{Void}, Int32, Ptr{UInt8}), regex, extra, what, buf) @@ -71,7 +71,7 @@ function info{T}( end function config{T}(what::Integer, ::Type{T}) - buf = Array(UInt8, sizeof(T)) + buf = zeros(UInt8, sizeof(T)) ret = ccall((:pcre_config, :libpcre), Int32, (Int32, Ptr{UInt8}), what, buf) @@ -84,7 +84,8 @@ end function compile(pattern::AbstractString, options::Integer) errstr = Array(Ptr{UInt8},1) - erroff = Array(Int32,1) + errstr[1] = C_NULL + erroff = zeros(Int32,1) re_ptr = ccall((:pcre_compile, :libpcre), Ptr{Void}, (Ptr{UInt8}, Int32, Ptr{Ptr{UInt8}}, Ptr{Int32}, Ptr{UInt8}), pattern, options, errstr, erroff, C_NULL) @@ -100,6 +101,7 @@ end function study(regex::Ptr{Void}, options::Integer) # NOTE: options should always be zero in current PCRE errstr = Array(Ptr{UInt8},1) + errstr[1] = C_NULL extra = ccall((:pcre_study, :libpcre), Ptr{Void}, (Ptr{Void}, Int32, Ptr{Ptr{UInt8}}), regex, options, errstr) diff --git a/base/pointer.jl b/base/pointer.jl index e94c43a3c7ff2..78f42983bebcb 100644 --- a/base/pointer.jl +++ b/base/pointer.jl @@ -19,16 +19,13 @@ convert(::Type{Ptr{UInt8}}, x::Symbol) = ccall(:jl_symbol_name, Ptr{UInt8}, (Any convert(::Type{Ptr{Int8}}, x::Symbol) = ccall(:jl_symbol_name, Ptr{Int8}, (Any,), x) convert(::Type{Ptr{UInt8}}, s::ByteString) = convert(Ptr{UInt8}, s.data) convert(::Type{Ptr{Int8}}, s::ByteString) = convert(Ptr{Int8}, s.data) +# convert strings to ByteString to pass as pointers +cconvert_gcroot(::Type{Ptr{UInt8}}, s::AbstractString) = bytestring(s) +cconvert_gcroot(::Type{Ptr{Int8}}, s::AbstractString) = bytestring(s) convert{T}(::Type{Ptr{T}}, a::Array{T}) = ccall(:jl_array_ptr, Ptr{T}, (Any,), a) convert(::Type{Ptr{Void}}, a::Array) = ccall(:jl_array_ptr, Ptr{Void}, (Any,), a) -# note: these definitions don't mean any AbstractArray is convertible to -# pointer. they just map the array element type to the pointer type for -# convenience in cases that work. -pointer{T}(x::AbstractArray{T}) = convert(Ptr{T},x) -pointer{T}(x::AbstractArray{T}, i::Integer) = convert(Ptr{T},x) + (i-1)*elsize(x) - # unsafe pointer to array conversions pointer_to_array(p, d::Integer, own=false) = pointer_to_array(p, (d,), own) function pointer_to_array{T,N}(p::Ptr{T}, dims::NTuple{N,Int}, own::Bool=false) @@ -53,7 +50,8 @@ unsafe_store!{T}(p::Ptr{T}, x) = pointerset(p, convert(T,x), 1) # convert a raw Ptr to an object reference, and vice-versa unsafe_pointer_to_objref(x::Ptr) = ccall(:jl_value_ptr, Any, (Ptr{Void},), x) -pointer_from_objref(x::Any) = ccall(:jl_value_ptr, Ptr{Void}, (Any,), x) +pointer_from_objref(x::ANY) = ccall(:jl_value_ptr, Ptr{Void}, (Any,), x) +data_pointer_from_objref(x::ANY) = pointer_from_objref(x)::Ptr{Void}+Core.sizeof(Int) integer(x::Ptr) = convert(UInt, x) unsigned(x::Ptr) = convert(UInt, x) diff --git a/base/refpointer.jl b/base/refpointer.jl new file mode 100644 index 0000000000000..876093c7248df --- /dev/null +++ b/base/refpointer.jl @@ -0,0 +1,121 @@ +### General Methods for Ref{T} type + +Base.eltype{T}(x::Type{Ref{T}}) = T +Base.convert{T}(::Type{Ref{T}}, x::Ref{T}) = x + +# create Ref objects for general object conversion +@inline Base.cconvert_gcroot{T}(::Type{Ref{T}}, x) = convert(Ref{T}, x) +@inline Base.cconvert{T}(::Type{Ref{T}}, x) = convert(Ptr{T}, x) + +### Methods for a Ref object that can store a single value + +type RefValue{T} <: Ref{T} + x::T + RefValue() = new() + RefValue(x) = new(x) +end +Base.convert{T}(::Type{Ref{T}}, x) = RefValue{T}(x) +Base.call{T}(::Type{Ref{T}}) = RefValue{T}() + +Ref(x::Ref) = x +Ref{T}(x::T) = RefValue{T}(x) +Ref{T}(x::Ptr{T}, i::Integer=1) = x + (i-1)*Core.sizeof(T) +Ref(x, i::Integer) = (i != 1 && error("Object only has one element"); Ref(x)) + +@inline function Base.convert{T}(P::Type{Ptr{T}}, b::RefValue{T}) + if isbits(T) + return convert(P, data_pointer_from_objref(b)) + else + return convert(P, data_pointer_from_objref(b.x)) + end +end +@inline function Base.convert(P::Type{Ptr{Any}}, b::RefValue{Any}) + return convert(P, data_pointer_from_objref(b)) +end +@inline Base.convert{T}(::Type{Ptr{Void}}, b::RefValue{T}) = Base.convert(Ptr{Void}, Base.convert(Ptr{T}, b)) + +### Methods for a Ref object that is backed by an array at index i + +# note: the following type definitions don't mean any AbstractArray is convertible to +# a data Ref. they just map the array element type to the pointer type for +# convenience in cases that work. +pointer{T}(x::AbstractArray{T}) = convert(Ptr{T},x) +pointer{T}(x::AbstractArray{T}, i::Integer) = convert(Ptr{T},x) + (i-1)*elsize(x) + +immutable RefArray{T, A<:AbstractArray} <: Ref{T} + x::A + i::Int + RefArray(x,i) = (@assert(eltype(A) == T); new(x,i)) +end +convert{T}(::Type{Ref{T}}, x::AbstractArray{T}) = RefArray{T,typeof(x)}(x, 1) +Ref{T}(x::AbstractArray{T}, i::Integer=1) = RefArray{T,typeof(x)}(x, i) + +@inline function Base.convert{T}(P::Type{Ptr{T}}, b::RefArray{T}) + if isbits(T) + convert(P, pointer(b.x, b.i)) + else + convert(P, data_pointer_from_objref(b.x[b.i])) + end +end +@inline function Base.convert(P::Type{Ptr{Any}}, b::RefArray{Any}) + return convert(P, pointer(b.x, b.i)) +end +@inline Base.convert{T}(::Type{Ptr{Void}}, b::RefArray{T}) = Base.convert(Ptr{Void}, Base.convert(Ptr{T}, b)) + +### Methods for a Ref object that is backed by an array at index (i...) + +immutable RefArrayND{T, A<:AbstractArray} <: Ref{T} + x::A + i::(Int...) + RefArrayND(x,i) = (@assert(eltype(A) == T); new(x,i)) +end +Ref{A<:AbstractArray}(x::A, i::Integer...) = RefArrayND{eltype(A),A}(x, i) +Ref{A<:AbstractArray}(x::A, i::(Integer...)) = RefArrayND{eltype(A),A}(x, i) + +@inline function Base.convert{T}(P::Type{Ptr{T}}, b::RefArrayND{T}) + if isbits(T) + convert(P, pointer(b.x, b.i...)) + else + convert(P, data_pointer_from_objref(b.x[b.i...])) + end +end +@inline function Base.convert(P::Type{Ptr{Any}}, b::RefArrayND{Any}) + return convert(P, pointer(b.x, b.i...)) +end +@inline Base.convert{T}(::Type{Ptr{Void}}, b::RefArrayND{T}) = Base.convert(Ptr{Void}, Base.convert(Ptr{T}, b)) + +### Methods for a Ref object that is backed by an array at index (Any...) + +immutable RefArrayI{T} <: Ref{T} + x::AbstractArray{T} + i::Tuple + RefArrayI(x,i::ANY) = (@assert(eltype(A) == T); new(x,i)) +end +Ref{T}(x::AbstractArray{T}, i...) = RefArrayI{T}(x, i) +Ref{T}(x::AbstractArray{T}, i::Tuple) = RefArrayI{T}(x, i) + +@inline function Base.convert{T}(P::Type{Ptr{T}}, b::RefArrayI{T}) + if isbits(T) + convert(P, pointer(b.x, b.i...)) + else + convert(P, data_pointer_from_objref(b.x[b.i...])) + end +end +@inline function Base.convert(P::Type{Ptr{Any}}, b::RefArrayI{Any}) + return convert(P, pointer(b.x, b.i...)) +end +@inline Base.convert{T}(::Type{Ptr{Void}}, b::RefArrayI{T}) = Base.convert(Ptr{Void}, Base.convert(Ptr{T}, b)) + +### + +Base.getindex(b::RefValue) = b.x +Base.getindex(b::RefArray) = b.x[b.i] +Base.getindex(b::RefArrayND) = b.x[b.i...] +Base.getindex(b::RefArrayI) = b.x[b.i...] + +Base.setindex!(b::RefValue, x) = (b.x = x; b) +Base.setindex!(b::RefArray, x) = (b.x[b.i] = x; b) +Base.setindex!(b::RefArrayND, x) = (b.x[b.i...] = x; b) +Base.setindex!(b::RefArrayI, x) = (b.x[b.i...] = x; b) + +### diff --git a/base/socket.jl b/base/socket.jl index ed76f649dc81d..24bb3b4e35073 100644 --- a/base/socket.jl +++ b/base/socket.jl @@ -593,7 +593,8 @@ const _sizeof_uv_interface_address = ccall(:jl_uv_sizeof_interface_address,Int32 function getipaddr() addr = Array(Ptr{UInt8},1) - count = Array(Int32,1) + addr[1] = C_NULL + count = zeros(Int32,1) lo_present = false err = ccall(:jl_uv_interface_addresses,Int32,(Ptr{Ptr{UInt8}},Ptr{Int32}),addr,count) addr, count = addr[1],count[1] diff --git a/base/sysimg.jl b/base/sysimg.jl index 3b7577ee0496a..9c6ece84c568f 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -67,6 +67,7 @@ include("dict.jl") include("set.jl") include("hashing.jl") include("iterator.jl") +include("refpointer.jl") #XXX: move this earlier when we don't need @inline annotations anymore # SIMD loops include("simdloop.jl") diff --git a/src/alloc.c b/src/alloc.c index 91fed046f4acf..c1bb50159036d 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -54,6 +54,7 @@ jl_datatype_t *jl_typeerror_type; jl_datatype_t *jl_methoderror_type; jl_datatype_t *jl_loaderror_type; jl_datatype_t *jl_undefvarerror_type; +jl_datatype_t *jl_ref_type; jl_datatype_t *jl_pointer_type; jl_datatype_t *jl_void_type; jl_datatype_t *jl_voidpointer_type; diff --git a/src/builtins.c b/src/builtins.c index 3ab3d2728dc4b..8da6f06c693b0 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1156,6 +1156,7 @@ void jl_init_primitives(void) add_builtin("IntrinsicFunction", (jl_value_t*)jl_intrinsic_type); add_builtin("Function", (jl_value_t*)jl_function_type); add_builtin("LambdaStaticData", (jl_value_t*)jl_lambda_info_type); + add_builtin("Ref", (jl_value_t*)jl_ref_type); add_builtin("Ptr", (jl_value_t*)jl_pointer_type); add_builtin("Box", (jl_value_t*)jl_box_type); add_builtin("Task", (jl_value_t*)jl_task_type); diff --git a/src/ccall.cpp b/src/ccall.cpp index 3a1eaf7f12642..50ceb0071626e 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -217,141 +217,21 @@ static Value *runtime_sym_lookup(PointerType *funcptype, char *f_lib, char *f_na llvmf = builder.CreateLoad(llvmgv); return builder.CreatePointerCast(llvmf,funcptype); } -// --- argument passing and scratch space utilities --- - -static Function *value_to_pointer_func; - -// TODO: per-thread -static char *temp_arg_area; -static const uint32_t arg_area_sz = 4196; -static uint32_t arg_area_loc; -#define N_TEMP_ARG_BLOCKS 1024 -static void *temp_arg_blocks[N_TEMP_ARG_BLOCKS]; -static uint32_t arg_block_n = 0; -static Function *save_arg_area_loc_func; -static Function *restore_arg_area_loc_func; - -extern "C" DLLEXPORT uint64_t save_arg_area_loc() -{ - return (((uint64_t)arg_block_n)<<32) | ((uint64_t)arg_area_loc); -} - -extern "C" DLLEXPORT void restore_arg_area_loc(uint64_t l) -{ - arg_area_loc = l&0xffffffff; - uint32_t ab = l>>32; - while (arg_block_n > ab) { - arg_block_n--; - free(temp_arg_blocks[arg_block_n]); - } -} - -static void *alloc_temp_arg_space(uint32_t sz) -{ - void *p; - if (arg_area_loc+sz > arg_area_sz) { -#ifdef JL_GC_MARKSWEEP - if (arg_block_n >= N_TEMP_ARG_BLOCKS) - jl_error("internal compiler error: out of temporary argument space in ccall"); - p = malloc(sz); - temp_arg_blocks[arg_block_n++] = p; -#else -#error "fixme" -#endif - } - else { - p = &temp_arg_area[arg_area_loc]; - arg_area_loc += sz; - } - return p; -} - -static void *alloc_temp_arg_copy(void *obj, uint32_t sz) -{ - void *p = alloc_temp_arg_space(sz); - memcpy(p, obj, sz); - return p; -} - -// this is a run-time function -// warning: cannot allocate memory except using alloc_temp_arg_space -extern "C" DLLEXPORT void *jl_value_to_pointer(jl_value_t *jt, jl_value_t *v, int argn, - int addressof) -{ - jl_value_t *jvt = (jl_value_t*)jl_typeof(v); - if (addressof) { - if (jvt == jt) { - if (jl_is_bitstype(jvt)) { - size_t osz = jl_datatype_size(jt); - return alloc_temp_arg_copy(jl_data_ptr(v), osz); - } - else if (!jl_is_tuple(jvt) && jl_is_leaf_type(jvt) && !jl_is_array_type(jvt)) { - return v + 1; - } - } - goto value_to_pointer_error; - } - else { - if (jl_is_cpointer_type(jvt) && jl_tparam0(jvt) == jt) { - return (void*)jl_unbox_voidpointer(v); - } - } - - if (((jl_value_t*)jl_uint8_type == jt || - (jl_value_t*)jl_int8_type == jt) && jl_is_byte_string(v)) { - return jl_string_data(v); - } - if (jl_is_array_type(jvt)) { - if (jl_tparam0(jl_typeof(v)) == jt || jt == (jl_value_t*)jl_bottom_type || - jt == (jl_value_t*)jl_void_type) { - return ((jl_array_t*)v)->data; - } - if (jl_is_cpointer_type(jt)) { - jl_array_t *ar = (jl_array_t*)v; - void **temp=(void**)alloc_temp_arg_space((1+jl_array_len(ar))*sizeof(void*)); - size_t i; - for(i=0; i < jl_array_len(ar); i++) { - temp[i] = jl_value_to_pointer(jl_tparam0(jt), - jl_arrayref(ar, i), argn, 0); - } - temp[i] = 0; - return temp; - } - } - - value_to_pointer_error: - std::map::iterator it = argNumberStrings.find(argn); - if (it == argNumberStrings.end()) { - std::stringstream msg; - msg << "argument "; - msg << argn; - argNumberStrings[argn] = msg.str(); - it = argNumberStrings.find(argn); - } - jl_value_t *targ=NULL, *pty=NULL; - JL_GC_PUSH2(&targ, &pty); - targ = (jl_value_t*)jl_tuple1(jt); - pty = (jl_value_t*)jl_apply_type((jl_value_t*)jl_pointer_type, - (jl_tuple_t*)targ); - jl_type_error_rt("ccall", (*it).second.c_str(), pty, v); - // doesn't return - return (jl_value_t*)jl_null; -} static Value *julia_to_native(Type *ty, jl_value_t *jt, Value *jv, jl_value_t *argex, bool addressOf, int argn, jl_codectx_t *ctx, - bool *mightNeedTempSpace, bool *needStackRestore) + bool *needStackRestore) { Type *vt = jv->getType(); if (ty == jl_pvalue_llvmt) { return boxed(jv,ctx); } - else if (ty == vt && !addressOf) { + if (ty == vt && !addressOf) { return jv; } - else if (vt != jl_pvalue_llvmt) { - // argument value is unboxed + if (vt != jl_pvalue_llvmt) { + // argument value is passed unboxed if (addressOf) { if (ty->isPointerTy() && ty->getContainedType(0)==vt) { // pass the address of an alloca'd thing, not a box @@ -370,62 +250,83 @@ static Value *julia_to_native(Type *ty, jl_value_t *jt, Value *jv, return builder.CreateBitCast(jv, ty); } } - // error. box for error handling. - jv = boxed(jv,ctx); + emit_error("ccall: argument type did not match declaration", ctx); } - else if (jl_is_cpointer_type(jt)) { + if (jl_is_tuple(jt)) { + return emit_unbox(ty,jv,jt); + } + if (jl_is_cpointer_type(jt) && addressOf) { assert(ty->isPointerTy()); + jl_value_t *ety = jl_tparam0(jt); jl_value_t *aty = expr_type(argex, ctx); - if (jl_is_array_type(aty) && - (jl_tparam0(jt) == jl_tparam0(aty) || jl_tparam0(jt) == (jl_value_t*)jl_bottom_type || - jl_tparam0(jt) == (jl_value_t*)jl_void_type)) { - // array to pointer - return builder.CreateBitCast(emit_arrayptr(jv), ty); - } - if (aty == (jl_value_t*)jl_ascii_string_type || aty == (jl_value_t*)jl_utf8_string_type) { - return builder.CreateBitCast(emit_arrayptr(emit_nthptr(jv,1,tbaa_const)), ty); - } - if (jl_is_structtype(aty) && jl_is_leaf_type(aty) && !jl_is_array_type(aty)) { - if (!addressOf) { - emit_error("ccall: expected & on argument", ctx); - return literal_pointer_val(jl_nothing); - } - return builder.CreateBitCast(emit_nthptr_addr(jv, (size_t)1), ty); // skip type tag field - } - *mightNeedTempSpace = true; - Value *p = builder.CreateCall4(prepare_call(value_to_pointer_func), - literal_pointer_val(jl_tparam0(jt)), jv, - ConstantInt::get(T_int32, argn), - ConstantInt::get(T_int32, (int)addressOf)); - return builder.CreateBitCast(p, ty); - } - else if (jl_is_structtype(jt)) { - if (addressOf) - jl_error("ccall: unexpected & on argument"); // the only "safe" thing to emit here is the expected struct - assert (ty->isStructTy() && (Type*)((jl_datatype_t*)jt)->struct_decl == ty); - jl_value_t *aty = expr_type(argex, ctx); - if (aty != jt) { + if (aty != ety && ety != (jl_value_t*)jl_any_type && jt != (jl_value_t*)jl_voidpointer_type) { std::stringstream msg; msg << "ccall argument "; msg << argn; - emit_typecheck(jv, jt, msg.str(), ctx); + emit_typecheck(jv, ety, msg.str(), ctx); } - //TODO: check instead that prefix matches - //if (!jl_is_structtype(aty)) - // emit_typecheck(emit_typeof(jv), (jl_value_t*)jl_struct_kind, "ccall: Struct argument called with something that isn't a struct", ctx); - // //safe thing would be to also check that jl_typeof(aty)->size > sizeof(ty) here and/or at runtime - Value *pjv = builder.CreateBitCast(emit_nthptr_addr(jv, (size_t)1), PointerType::get(ty,0)); - return builder.CreateLoad(pjv, false); - } - else if (jl_is_tuple(jt)) { - return emit_unbox(ty,jv,jt); + if (jl_is_mutable_datatype(ety)) { + // no copy, just reference the data field + return builder.CreateBitCast(emit_nthptr_addr(jv, (size_t)1), ty); // skip type tag field + } + else if (jl_is_immutable_datatype(ety) && jt != (jl_value_t*)jl_voidpointer_type) { + // yes copy + Value *nbytes; + if (jl_is_leaf_type(ety)) + nbytes = ConstantInt::get(T_int32, jl_datatype_size(ety)); + else + nbytes = tbaa_decorate(tbaa_datatype, builder.CreateLoad( + builder.CreateGEP(builder.CreatePointerCast(emit_typeof(jv), T_pint32), + ConstantInt::get(T_size, offsetof(jl_datatype_t,size)/4)), + false)); + *needStackRestore = true; + AllocaInst *ai = builder.CreateAlloca(T_int8, nbytes); + ai->setAlignment(16); + builder.CreateMemCpy(ai, builder.CreateBitCast(emit_nthptr_addr(jv, (size_t)1), T_pint8), nbytes, 1); + return builder.CreateBitCast(ai, ty); + } + // emit maybe copy + *needStackRestore = true; + Value *jvt = emit_typeof(jv); + BasicBlock *mutableBB = BasicBlock::Create(getGlobalContext(),"is-mutable",ctx->f); + BasicBlock *immutableBB = BasicBlock::Create(getGlobalContext(),"is-immutable",ctx->f); + BasicBlock *afterBB = BasicBlock::Create(getGlobalContext(),"after",ctx->f); + Value *ismutable = builder.CreateTrunc( + tbaa_decorate(tbaa_datatype, builder.CreateLoad( + builder.CreateGEP(builder.CreatePointerCast(jvt, T_pint8), + ConstantInt::get(T_size, offsetof(jl_datatype_t,mutabl))), + false)), + T_int1); + builder.CreateCondBr(ismutable, mutableBB, immutableBB); + builder.SetInsertPoint(mutableBB); + Value *p1 = builder.CreatePointerCast(emit_nthptr_addr(jv, (size_t)1), ty); // skip type tag field + builder.CreateBr(afterBB); + builder.SetInsertPoint(immutableBB); + Value *nbytes = tbaa_decorate(tbaa_datatype, builder.CreateLoad( + builder.CreateGEP(builder.CreatePointerCast(jvt, T_pint32), + ConstantInt::get(T_size, offsetof(jl_datatype_t,size)/4)), + false)); + AllocaInst *ai = builder.CreateAlloca(T_int8, nbytes); + ai->setAlignment(16); + builder.CreateMemCpy(ai, builder.CreatePointerCast(emit_nthptr_addr(jv, (size_t)1), T_pint8), nbytes, 1); + Value *p2 = builder.CreatePointerCast(ai, ty); + builder.CreateBr(afterBB); + builder.SetInsertPoint(afterBB); + PHINode *p = builder.CreatePHI(ty, 2); + p->addIncoming(p1, mutableBB); + p->addIncoming(p2, immutableBB); + return p; + } + if (addressOf) + jl_error("ccall: unexpected & on argument"); // the only "safe" thing to emit here is the expected struct + assert(jl_is_datatype(jt)); + jl_value_t *aty = expr_type(argex, ctx); + if (aty != jt) { + std::stringstream msg; + msg << "ccall argument "; + msg << argn; + emit_typecheck(jv, jt, msg.str(), ctx); } - // TODO: error for & with non-pointer argument type - assert(jl_is_bitstype(jt)); - std::stringstream msg; - msg << "ccall argument "; - msg << argn; - emit_typecheck(jv, jt, msg.str(), ctx); Value *p = data_pointer(jv); return builder.CreateLoad(builder.CreateBitCast(p, PointerType::get(ty,0)), @@ -691,7 +592,7 @@ static Value *emit_llvmcall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) } #endif bool mightNeedTempSpace = false; - argvals[i] = julia_to_native(t,tti,arg,argi,false,i,ctx,&mightNeedTempSpace,&mightNeedTempSpace); + argvals[i] = julia_to_native(t,tti,arg,argi,false,i,ctx,&mightNeedTempSpace); } Function *f; @@ -869,8 +770,14 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) ": ccall: missing return type"; jl_error(msg.c_str()); } - if (rt == (jl_value_t*)jl_pointer_type) - jl_error("ccall: return type Ptr should have an element type, Ptr{T}"); + 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 Box{Any} is invalid. use Ptr{Any} instead."); + rt = (jl_value_t*)jl_any_type; // convert return type to jl_value_t* + } JL_TYPECHK(ccall, type, rt); Type *lrt = julia_struct_to_llvm(rt); @@ -916,54 +823,65 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) paramattrs.push_back(AttrBuilder()); #endif jl_value_t *tti = jl_tupleref(tt,i); - if (tti == (jl_value_t*)jl_pointer_type) - jl_error("ccall: argument type Ptr should have an element type, Ptr{T}"); + if (jl_is_vararg_type(tti)) { isVa = true; tti = jl_tparam0(tti); } - if (jl_is_bitstype(tti)) { - // see pull req #978. need to annotate signext/zeroext for - // small integer arguments. - jl_datatype_t *bt = (jl_datatype_t*)tti; - if (bt->size < 4) { - if (jl_signed_type == NULL) { - jl_signed_type = jl_get_global(jl_core_module,jl_symbol("Signed")); - } + + Type *t = NULL; + if (jl_is_abstract_ref_type(tti)) { + tti = jl_tparam0(tti); + if (jl_is_typevar(tti)) + jl_error("ccall: argument type Ref should have an element type, not Ref{_<:T}"); + t = T_pint8; + } + 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. + jl_datatype_t *bt = (jl_datatype_t*)tti; + if (bt->size < 4) { + if (jl_signed_type == NULL) { + jl_signed_type = jl_get_global(jl_core_module,jl_symbol("Signed")); + } #if LLVM33 - Attribute::AttrKind av; + Attribute::AttrKind av; #elif LLVM32 - Attributes::AttrVal av; + Attributes::AttrVal av; #else - Attribute::AttrConst av; + Attribute::AttrConst av; #endif #if LLVM32 && !LLVM33 - if (jl_signed_type && jl_subtype(tti, jl_signed_type, 0)) - av = Attributes::SExt; - else - av = Attributes::ZExt; + if (jl_signed_type && jl_subtype(tti, jl_signed_type, 0)) + av = Attributes::SExt; + else + av = Attributes::ZExt; #else - if (jl_signed_type && jl_subtype(tti, jl_signed_type, 0)) - av = Attribute::SExt; - else - av = Attribute::ZExt; + if (jl_signed_type && jl_subtype(tti, jl_signed_type, 0)) + av = Attribute::SExt; + else + av = Attribute::ZExt; #endif #if LLVM32 || LLVM33 - paramattrs[i+sret].addAttribute(av); + paramattrs[i+sret].addAttribute(av); #else - attrs.push_back(AttributeWithIndex::get(i+1+sret, av)); + attrs.push_back(AttributeWithIndex::get(i+1+sret, av)); #endif + } + } + t = julia_struct_to_llvm(tti); + if (t == NULL || t == T_void) { + JL_GC_POP(); + std::stringstream msg; + msg << "ccall: the type of argument "; + msg << i+1; + msg << " doesn't correspond to a C type"; + emit_error(msg.str(), ctx); + return literal_pointer_val(jl_nothing); } - } - Type *t = julia_struct_to_llvm(tti); - if (t == NULL || t == T_void) { - JL_GC_POP(); - std::stringstream msg; - msg << "ccall: the type of argument "; - msg << i+1; - msg << " doesn't correspond to a C type"; - emit_error(msg.str(), ctx); - return literal_pointer_val(jl_nothing); } fargt.push_back(t); if (!isVa) @@ -1019,6 +937,9 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) if (jl_is_expr(argi) && ((jl_expr_t*)argi)->head == amp_sym) { addressOf = true; argi = jl_exprarg(argi,0); + } else if (jl_is_abstract_ref_type(jl_tupleref(tt,0))) { + if (!jl_is_cpointer_type(expr_type(argi, 0))) + addressOf = true; } Value *ary; Type *largty = fargt[0]; @@ -1045,7 +966,6 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) // save place before arguments, for possible insertion of temp arg // area saving code. - Value *saveloc=NULL; Value *stacksave=NULL; BasicBlock::InstListType &instList = builder.GetInsertBlock()->getInstList(); Instruction *savespot; @@ -1076,8 +996,6 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) fargt_sig[0]); } int last_depth = ctx->argDepth; - int nargty = jl_tuple_len(tt); - bool needTempSpace = false; bool needStackRestore = false; for(i=4; i < nargs+1; i+=2) { size_t ai = (i-4)/2; @@ -1099,7 +1017,17 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) } Value *arg; bool needroot = false; - if (largty == jl_pvalue_llvmt || largty->isStructTy()) { + if (jl_is_abstract_ref_type(jargty)) { + arg = emit_unboxed((jl_value_t*)argi, ctx); + if (arg->getType() == jl_pvalue_llvmt) { + emit_cpointercheck(arg, "ccall: argument to Ref{T} is not a pointer", ctx); + arg = emit_unbox(largty, arg, (jl_value_t*)jl_voidpointer_type); + } + if (arg->getType() != T_pint8) + arg = builder.CreatePointerCast(arg, T_pint8); + jargty = (jl_value_t*)jl_voidpointer_type; + } + else if (largty == jl_pvalue_llvmt || largty->isStructTy()) { arg = emit_expr(argi, ctx, true); if (largty == jl_pvalue_llvmt && arg->getType() != jl_pvalue_llvmt) { arg = boxed(arg,ctx); @@ -1129,11 +1057,9 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) } #endif - bool mightNeed=false; bool nSR=false; argvals[ai+sret] = julia_to_native(largty, jargty, arg, argi, addressOf, - ai+1, ctx, &mightNeed, &nSR); - needTempSpace |= mightNeed; + ai+1, ctx, &nSR); needStackRestore |= nSR; } @@ -1185,16 +1111,6 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) } } - if (needTempSpace) { - // save temp argument area stack pointer - // TODO: inline this - saveloc = CallInst::Create(prepare_call(save_arg_area_loc_func)); - if (savespot) - instList.insertAfter(savespot, (Instruction*)saveloc); - else - instList.push_front((Instruction*)saveloc); - savespot = (Instruction*)saveloc; - } if (needStackRestore) { stacksave = CallInst::Create(Intrinsic::getDeclaration(jl_Module, Intrinsic::stacksave)); @@ -1234,11 +1150,6 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) Intrinsic::stackrestore), stacksave); } - if (needTempSpace) { - // restore temp argument area stack pointer - assert(saveloc != NULL); - builder.CreateCall(prepare_call(restore_arg_area_loc_func), saveloc); - } ctx->argDepth = last_depth; if (0) { // Enable this to turn on SSPREQ (-fstack-protector) on the function containing this ccall #if LLVM32 && !LLVM33 diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 2e71c660c599e..a13f62c099ae9 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -547,7 +547,7 @@ DLLEXPORT Type *julia_type_to_llvm(jl_value_t *jt) if (lt == NULL) return NULL; if (lt == T_void) - lt = T_int8; + return T_pint8; return PointerType::get(lt, 0); } if (jl_is_bitstype(jt)) { @@ -1552,6 +1552,7 @@ static Value *init_bits_value(Value *newv, Value *jt, Type *t, Value *v) // allocate a box where the type might not be known at compile time static Value *allocate_box_dynamic(Value *jlty, Value *nb, Value *v) { + // TODO: allocate on the stack if !envescapes if (v->getType()->isPointerTy()) { v = builder.CreatePtrToInt(v, T_size); } diff --git a/src/codegen.cpp b/src/codegen.cpp index 2b372ef1485d8..b51bfc531ae1a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4274,7 +4274,7 @@ static Function *emit_function(jl_lambda_info_t *lam, bool cstyle) builder.CreateAlloca(T_int8, ConstantInt::get(T_int32, sizeof(jl_handler_t))); - handlr->setAlignment(128); // bits == 16 bytes + handlr->setAlignment(16); handlers[labl] = handlr; } } @@ -5466,36 +5466,6 @@ extern "C" void jl_init_codegen(void) box64_func = boxfunc_llvm(ft2arg(jl_pvalue_llvmt, jl_pvalue_llvmt, T_int64), "jl_box64", (void*)&jl_box64, m); - std::vector toptrargs(0); - toptrargs.push_back(jl_pvalue_llvmt); - toptrargs.push_back(jl_pvalue_llvmt); - toptrargs.push_back(T_int32); - toptrargs.push_back(T_int32); - value_to_pointer_func = - Function::Create(FunctionType::get(T_pint8, toptrargs, false), - Function::ExternalLinkage, "jl_value_to_pointer", - m); - add_named_global(value_to_pointer_func, - (void*)&jl_value_to_pointer); - - temp_arg_area = (char*)malloc(arg_area_sz); - arg_area_loc = 0; - - std::vector noargs(0); - save_arg_area_loc_func = - Function::Create(FunctionType::get(T_uint64, noargs, false), - Function::ExternalLinkage, "save_arg_area_loc", - m); - add_named_global(save_arg_area_loc_func, - (void*)&save_arg_area_loc); - - restore_arg_area_loc_func = - Function::Create(ft1arg(T_void, T_uint64), - Function::ExternalLinkage, "restore_arg_area_loc", - m); - add_named_global(restore_arg_area_loc_func, - (void*)&restore_arg_area_loc); - typeToTypeId = jl_alloc_cell_1d(16); } diff --git a/src/dump.c b/src/dump.c index 645efba41e3c9..c2ba79fdb7a86 100644 --- a/src/dump.c +++ b/src/dump.c @@ -904,9 +904,9 @@ static jl_value_t *jl_deserialize_datatype(ios_t *s, int pos, jl_value_t **loc) dt->super = (jl_datatype_t*)jl_deserialize_value(s, (jl_value_t**)&dt->super); gc_wb(dt, dt->super); if (datatype_list) { - if (dt->name == jl_array_type->name || dt->name == jl_pointer_type->name || - dt->name == jl_type_type->name || dt->name == jl_vararg_type->name || - dt->name == jl_abstractarray_type->name || + if (dt->name == jl_array_type->name || dt->name == jl_ref_type->name || + dt->name == jl_pointer_type->name || dt->name == jl_type_type->name || + dt->name == jl_vararg_type->name || dt->name == jl_abstractarray_type->name || dt->name == jl_densearray_type->name) { // builtin types are not serialized, so their caches aren't // explicitly saved. so we reconstruct the caches of builtin @@ -1843,17 +1843,19 @@ void jl_init_serializer(void) jl_box_int64(48), jl_box_int64(49), jl_box_int64(50), jl_box_int64(51), jl_box_int64(52), jl_box_int64(53), #endif - jl_labelnode_type, jl_linenumbernode_type, jl_gotonode_type, - jl_quotenode_type, jl_topnode_type, jl_type_type, jl_bottom_type, - jl_pointer_type, jl_vararg_type, jl_ntuple_type, - jl_abstractarray_type, jl_densearray_type, jl_box_type, jl_void_type, - jl_typector_type, jl_typename_type, jl_task_type, jl_uniontype_type, - jl_typetype_type, jl_typetype_tvar, jl_ANY_flag, jl_array_any_type, - jl_intrinsic_type, jl_method_type, jl_methtable_type, - jl_voidpointer_type, jl_newvarnode_type, jl_array_symbol_type, - jl_tupleref(jl_tuple_type,0), - - jl_symbol_type->name, jl_gensym_type->name, jl_pointer_type->name, + jl_labelnode_type, jl_linenumbernode_type, + jl_gotonode_type, jl_quotenode_type, jl_topnode_type, + jl_type_type, jl_bottom_type, jl_ref_type, jl_pointer_type, + jl_vararg_type, jl_ntuple_type, jl_abstractarray_type, + jl_densearray_type, jl_box_type, jl_void_type, + jl_typector_type, jl_typename_type, + jl_task_type, jl_uniontype_type, jl_typetype_type, jl_typetype_tvar, + jl_ANY_flag, jl_array_any_type, jl_intrinsic_type, jl_method_type, + jl_methtable_type, jl_voidpointer_type, jl_newvarnode_type, + jl_array_symbol_type, jl_tupleref(jl_tuple_type,0), + + jl_symbol_type->name, jl_gensym_type->name, + jl_ref_type->name, jl_pointer_type->name, jl_datatype_type->name, jl_uniontype_type->name, jl_array_type->name, jl_expr_type->name, jl_typename_type->name, jl_type_type->name, jl_methtable_type->name, jl_method_type->name, jl_tvar_type->name, diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 3faefc5f35e04..7813b5d4f9cc4 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -162,7 +162,7 @@ static Constant *julia_const_to_llvm(jl_value_t *e) // If we have a floating point type that's not hardware supported, just treat it like an integer for LLVM purposes } Constant *asInt = ConstantInt::get(IntegerType::get(jl_LLVMContext,8*nb),val); - if (jl_is_cpointer_type(bt)) { + if (jl_is_cpointer_type(jt)) { return ConstantExpr::getIntToPtr(asInt, julia_type_to_llvm((jl_value_t*)bt)); } return asInt; diff --git a/src/jltypes.c b/src/jltypes.c index 450dc5c8c3d39..17f994d64711a 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3281,22 +3281,27 @@ void jl_init_types(void) jl_intrinsic_type = jl_new_bitstype((jl_value_t*)jl_symbol("IntrinsicFunction"), jl_any_type, jl_null, 32); + tv = jl_tuple1(tvar("T")); + jl_ref_type = + jl_new_abstracttype((jl_value_t*)jl_symbol("Ref"), jl_any_type, tv); + tv = jl_tuple1(tvar("T")); jl_pointer_type = - jl_new_bitstype((jl_value_t*)jl_symbol("Ptr"), jl_any_type, tv, + jl_new_bitstype((jl_value_t*)jl_symbol("Ptr"), + (jl_datatype_t*)jl_apply_type((jl_value_t*)jl_ref_type, tv), tv, sizeof(void*)*8); // Type{T} jl_typetype_tvar = jl_new_typevar(jl_symbol("T"), (jl_value_t*)jl_bottom_type,(jl_value_t*)jl_any_type); jl_typetype_type = (jl_datatype_t*)jl_apply_type((jl_value_t*)jl_type_type, - jl_tuple(1,jl_typetype_tvar)); + jl_tuple1(jl_typetype_tvar)); jl_ANY_flag = (jl_value_t*)tvar("ANY"); // complete builtin type metadata jl_value_t *pointer_void = jl_apply_type((jl_value_t*)jl_pointer_type, - jl_tuple(1,jl_void_type)); + jl_tuple1(jl_void_type)); jl_voidpointer_type = (jl_datatype_t*)pointer_void; jl_tupleset(jl_datatype_type->types, 6, jl_int32_type); jl_tupleset(jl_datatype_type->types, 7, (jl_value_t*)jl_bool_type); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index c95a3bfeb1ac8..33d313165de60 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -994,20 +994,6 @@ ;; insert calls to convert() in ccall, and pull out expressions that might ;; need to be rooted before conversion. (define (lower-ccall name RT atypes args) - (define (ccall-conversion T x) - (cond ((eq? T 'Any) x) - ((and (pair? x) (eq? (car x) '&)) - `(& (call (top ptr_arg_convert) ,T ,(cadr x)))) - (else - `(call (top cconvert) ,T ,x)))) - (define (argument-root a) - ;; something to keep rooted for this argument - (cond ((and (pair? a) (eq? (car a) '&)) - (argument-root (cadr a))) - ((and (pair? a) (sym-dot? a)) - (cadr a)) - ((symbol-like? a) a) - (else 0))) (let loop ((F atypes) ;; formals (A args) ;; actuals (stmts '()) ;; initializers @@ -1019,28 +1005,16 @@ ,@A)) (let* ((a (car A)) (isseq (and (pair? (car F)) (eq? (caar F) '...))) - (ty (if isseq (cadar F) (car F))) - (rt (if (eq? ty 'Any) - 0 - (argument-root a))) - (ca (cond ((eq? ty 'Any) - a) - ((and (pair? a) (eq? (car a) '&)) - (if (and (pair? (cadr a)) (not (sym-dot? (cadr a)))) - (let ((g (make-jlgensym))) - (begin - (set! stmts (cons `(= ,g ,(cadr a)) stmts)) - `(& ,g))) - a)) - ((and (pair? a) (not (sym-dot? a)) (not (quoted? a))) - (let ((g (make-jlgensym))) - (begin - (set! stmts (cons `(= ,g ,a) stmts)) - g))) - (else - a)))) - (loop (if isseq F (cdr F)) (cdr A) stmts - (list* rt (ccall-conversion ty ca) C)))))) + (ty (if isseq (cadar F) (car F)))) + (if (eq? ty 'Any) + (loop (if isseq F (cdr F)) (cdr A) stmts (list* 0 a C)) + (let* ((g (make-jlgensym)) + (isamp (and (pair? a) (eq? (car a) '&))) + (a (if isamp (cadr a) a)) + (stmts (cons `(= ,g (call (top ,(if isamp 'ptr_arg_cconvert_gcroot 'cconvert_gcroot)) ,ty ,a)) stmts)) + (ca `(call (top ,(if isamp 'ptr_arg_cconvert 'cconvert)) ,ty ,g))) + (loop (if isseq F (cdr F)) (cdr A) stmts + (list* g (if isamp `(& ,ca) ca) C)))))))) (define (block-returns? e) (if (assignment? e) diff --git a/src/julia.h b/src/julia.h index 1c99eb8d4cc62..60cfb93acf2f9 100644 --- a/src/julia.h +++ b/src/julia.h @@ -367,6 +367,7 @@ extern DLLEXPORT jl_datatype_t *jl_number_type; extern DLLEXPORT jl_datatype_t *jl_void_type; extern DLLEXPORT jl_datatype_t *jl_voidpointer_type; extern DLLEXPORT jl_datatype_t *jl_pointer_type; +extern DLLEXPORT jl_datatype_t *jl_ref_type; extern DLLEXPORT jl_value_t *jl_array_uint8_type; extern DLLEXPORT jl_value_t *jl_array_any_type; @@ -646,12 +647,30 @@ STATIC_INLINE int jl_is_array(void *v) return jl_is_array_type(t); } -STATIC_INLINE int jl_is_cpointer_type(void *t) +STATIC_INLINE int jl_is_cpointer_type(jl_value_t *t) { return (jl_is_datatype(t) && ((jl_datatype_t*)(t))->name == jl_pointer_type->name); } +STATIC_INLINE int jl_is_abstract_ref_type(jl_value_t *t) +{ + return (jl_is_datatype(t) && + ((jl_datatype_t*)(t))->name == jl_ref_type->name); +} + +STATIC_INLINE jl_value_t *jl_is_ref_type(jl_value_t *t) +{ + if (!jl_is_datatype(t)) return 0; + jl_datatype_t *dt = (jl_datatype_t*)t; + while (dt != jl_any_type && dt->name != dt->super->name) { + if (dt->name == jl_ref_type->name) + return (jl_value_t*)dt; + dt = dt->super; + } + return 0; +} + STATIC_INLINE int jl_is_vararg_type(jl_value_t *v) { return (jl_is_datatype(v) && diff --git a/test/Makefile b/test/Makefile index 8c97294bc5bc1..f83b90bffff5b 100644 --- a/test/Makefile +++ b/test/Makefile @@ -5,7 +5,7 @@ TESTS = all linalg $(filter-out TestHelpers runtests testdefs,$(subst .jl,,$(wil default: all -$(TESTS) :: +$(TESTS): @$(call PRINT_JULIA, $(call spawn,$(JULIA_EXECUTABLE)) --check-bounds=yes --startup-file=no ./runtests.jl $@) perf: @@ -19,6 +19,7 @@ clean: libccalltest.$(SHLIB_EXT): ccalltest.c $(CC) $(CFLAGS) $(DEBUGFLAGS) -O3 $< -fPIC -shared -o $@ $(LDFLAGS) -DCC=$(CC) +ccall: libccalltest.$(SHLIB_EXT) ccalltest: ccalltest.c $(CC) $(CFLAGS) $(DEBUGFLAGS) -O3 $< -o $@ $(LDFLAGS) -DCC=$(CC) diff --git a/test/ccall.jl b/test/ccall.jl index 25221003e0dcc..67f3be04ade7a 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -1,3 +1,24 @@ -ccall_test_func(x) = ccall((:testUcharX, "./libccalltest"), Int32, (UInt8,), x) +ccall_test_func(x) = ccall((:testUcharX, "./libccalltest"), Int32, (UInt8,), x % UInt8) @test ccall_test_func(3) == 1 @test ccall_test_func(259) == 1 + +ccall_echo_func{T,U}(x, ::Type{T}, ::Type{U}) = ccall((:test_echo_p, "./libccalltest"), T, (U,), x) + +type IntLike + x::Int +end +@test unsafe_load(ccall_echo_func(132, Ptr{Int}, Ref{Int})) === 132 +@test unsafe_load(ccall_echo_func(Ref(921), Ptr{Int}, Ref{Int})) === 921 +@test unsafe_load(ccall_echo_func(IntLike(993), Ptr{Int}, Ref{IntLike})) === 993 +@test unsafe_load(ccall_echo_func(IntLike(881), Ptr{IntLike}, Ref{IntLike})).x === 881 +@test ccall_echo_func(532, Int, Int) === 532 +@test ccall_echo_func(164, IntLike, Int).x === 164 +@test ccall_echo_func(IntLike(828), Int, IntLike) === 828 +@test ccall_echo_func(913, Any, Any) === 913 +@test unsafe_pointer_to_objref(ccall_echo_func(553, Ptr{Any}, Any)) === 553 +@test ccall_echo_func(124, Ref{Int}, Any) === 124 +@test unsafe_load(ccall_echo_func(422, Ptr{Any}, Ref{Any})) === 422 + +@test unsafe_load(ccall_echo_func([383], Ptr{Int}, Ref{Int})) === 383 +@test unsafe_load(ccall_echo_func(Ref([144,172],2), Ptr{Int}, Ref{Int})) === 172 +#@test unsafe_load(ccall_echo_func(Ref([8],1,1), Ptr{Int}, Ref{Int})) === 8 diff --git a/test/ccalltest.c b/test/ccalltest.c index e88304d991a49..802e21713d2c2 100644 --- a/test/ccalltest.c +++ b/test/ccalltest.c @@ -2,7 +2,7 @@ int xs[300] = {0,0,0,1,0}; -int __attribute((noinline)) testUcharX(unsigned char x) { +int __attribute__((noinline)) testUcharX(unsigned char x) { return xs[x]; } @@ -22,3 +22,6 @@ int main() { xstr(CC), xs[a], xs[b], testUcharX(a), b, testUcharX(b), fptr(a), fptr(b)); } +void *test_echo_p(void *p) { + return p; +}