From 468cb7131b08c1e6d0c34daddf27c1df71902bbb Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 9 Mar 2015 19:37:40 -0400 Subject: [PATCH 01/14] fix #10444 --- src/debuginfo.cpp | 2 +- src/task.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index 3348364dd0e29..68d8b5a13bf7c 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -352,7 +352,7 @@ class JuliaJITEventListener: public JITEventListener #endif ObjectInfo tmp = {objfile, (size_t)Size #ifdef LLVM36 - ,SectionAddr + ,(size_t)SectionAddr #endif #ifdef _OS_DARWIN_ ,strndup(sName.data(), sName.size()) diff --git a/src/task.c b/src/task.c index 48f3f0ee1a93f..15de40c7d1c70 100644 --- a/src/task.c +++ b/src/task.c @@ -590,12 +590,13 @@ DLLEXPORT size_t rec_backtrace_ctx(ptrint_t *data, size_t maxsize, CONTEXT *Cont size_t n = 0; while (n < maxsize) { #ifndef _CPU_X86_64_ + data[n++] = (intptr_t)stk.AddrPC.Offset; BOOL result = StackWalk64(MachineType, GetCurrentProcess(), hMainThread, &stk, Context, NULL, JuliaFunctionTableAccess64, JuliaGetModuleBase64, NULL); if (!result) break; - data[n++] = (intptr_t)stk.AddrPC.Offset; #else + data[n++] = (intptr_t)Context->Rip; DWORD64 ImageBase = JuliaGetModuleBase64(GetCurrentProcess(), Context->Rip); if (!ImageBase) break; @@ -622,7 +623,6 @@ DLLEXPORT size_t rec_backtrace_ctx(ptrint_t *data, size_t maxsize, CONTEXT *Cont } if (!Context->Rip) break; - data[n++] = (intptr_t)Context->Rip; #endif } #if !defined(_CPU_X86_64_) From 9053bfff983bf758539968dd3322c65b4281e08c Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 7 Mar 2015 14:28:55 -0500 Subject: [PATCH 02/14] proper ABI implementation support for ccall, and start work on the same for cfunction --- LICENSE.md | 1 + Makefile | 6 +- base/boot.jl | 7 +- base/reflection.jl | 5 + base/special/erf.jl | 3 - src/abi_llvm.cpp | 38 ++++ src/abi_win64.cpp | 68 ++++++ src/abi_x86.cpp | 61 +++++ src/abi_x86_64.cpp | 192 ++++++++++++++++ src/alloc.c | 3 +- src/ccall.cpp | 538 ++++++++++++++++++++++++++++++++------------ src/cgutils.cpp | 157 +++++++++++++ src/codegen.cpp | 327 +++++++++++++-------------- src/dump.c | 5 +- src/init.c | 1 + src/jltypes.c | 1 + src/julia.h | 12 +- src/sys.c | 13 ++ test/.gitignore | 2 + test/Makefile | 10 +- test/ccall.jl | 120 +++++++++- test/ccalltest.c | 407 ++++++++++++++++++++++++++++++++- test/choosetests.jl | 2 +- test/math.jl | 15 +- 24 files changed, 1645 insertions(+), 349 deletions(-) create mode 100644 src/abi_llvm.cpp create mode 100644 src/abi_win64.cpp create mode 100644 src/abi_x86.cpp create mode 100644 src/abi_x86_64.cpp diff --git a/LICENSE.md b/LICENSE.md index 4448d20f0d93d..75812c87bc712 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -70,6 +70,7 @@ The following components of Julia's standard library have separate licenses: Julia builds the following libraries by default, but does not use them itself: - [RMATH](http://www.r-project.org/Licenses/) +- [LDC](https://github.com/ldc-developers/ldc/blob/master/LICENSE) Julia's build process uses the following external tools: diff --git a/Makefile b/Makefile index 7864926975313..9abcdee9d7e19 100644 --- a/Makefile +++ b/Makefile @@ -69,6 +69,9 @@ julia-deps: git-submodules | $(DIRS) $(build_datarootdir)/julia/base $(build_da julia-base: julia-deps @$(MAKE) $(QUIET_MAKE) -C base +julia-libccalltest: + @$(MAKE) $(QUIET_MAKE) -C test libccalltest + julia-src-%: julia-deps @$(MAKE) $(QUIET_MAKE) -C src libjulia-$* @@ -78,7 +81,7 @@ julia-ui-%: julia-src-% julia-sysimg-% : julia-ui-% julia-base @$(MAKE) $(QUIET_MAKE) LD_LIBRARY_PATH=$(build_libdir):$(LD_LIBRARY_PATH) JULIA_EXECUTABLE="$(JULIA_EXECUTABLE_$*)" $(build_private_libdir)/sys.$(SHLIB_EXT) -julia-debug julia-release : julia-% : julia-symlink-% julia-sysimg-% +julia-debug julia-release : julia-% : julia-symlink-% julia-sysimg-% julia-libccalltest debug release : % : julia-% @@ -455,6 +458,7 @@ clean: | $(CLEAN_TARGETS) @$(MAKE) -C doc clean @$(MAKE) -C src clean @$(MAKE) -C ui clean + @$(MAKE) -C test clean @rm -f julia @rm -f *~ *# *.tar.gz @rm -f $(build_bindir)/stringreplace \ diff --git a/base/boot.jl b/base/boot.jl index 9f8e2f2f2c776..c39adeaf8a8ed 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -125,9 +125,10 @@ export Box, Function, IntrinsicFunction, LambdaStaticData, Method, MethodTable, Module, Symbol, Task, Array, GenSym, # numeric types - Bool, FloatingPoint, Float16, Float32, Float64, Number, Integer, Int, Int8, Int16, - Int32, Int64, Int128, Ref, Ptr, Real, Signed, UInt, UInt8, UInt16, UInt32, - UInt64, UInt128, Unsigned, + Real, Complex, Number, Integer, Bool, Ref, Ptr, + FloatingPoint, Float16, Float32, Float64, + Signed, Int, Int8, Int16, Int32, Int64, Int128, + Unsigned, UInt, UInt8, UInt16, UInt32, UInt64, UInt128, # string types Char, ASCIIString, ByteString, DirectIndexString, AbstractString, UTF8String, # errors diff --git a/base/reflection.jl b/base/reflection.jl index 66ad762dcfb34..29cd28482b138 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -215,3 +215,8 @@ function function_module(f, types) end m[1].func.code.module end + +# + +type_alignment(x::DataType) = ccall(:jl_get_alignment,Csize_t,(Any,),x) +field_offset(x::DataType,idx) = ccall(:jl_get_field_offset,Csize_t,(Any,Int32),x,idx) diff --git a/base/special/erf.jl b/base/special/erf.jl index e07011b3b4737..af4b0a86665ab 100644 --- a/base/special/erf.jl +++ b/base/special/erf.jl @@ -1,5 +1,3 @@ -@unix_only if WORD_SIZE == 64 -# TODO: complex return only on 64-bit for now for f in (:erf, :erfc, :erfcx, :erfi, :Dawson) fname = (f === :Dawson) ? :dawson : f @eval begin @@ -8,7 +6,6 @@ for f in (:erf, :erfc, :erfcx, :erfi, :Dawson) ($fname)(z::Complex) = ($fname)(Complex128(z)) end end -end for f in (:erfcx, :erfi, :Dawson) fname = (f === :Dawson) ? :dawson : f @eval begin diff --git a/src/abi_llvm.cpp b/src/abi_llvm.cpp new file mode 100644 index 0000000000000..9cd6404bd2fc2 --- /dev/null +++ b/src/abi_llvm.cpp @@ -0,0 +1,38 @@ +//===-- abi_llvm.cpp - LLVM Target ABI description --------------*- C++ -*-===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the BSD-style LDC license. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// +// +// This ABI implementation does whatever LLVM decides is fine. +// It can be useful when first porting Julia to a new platform +// (until a platform-specific implementation can be developed). +// +//===----------------------------------------------------------------------===// + + +typedef bool AbiState; +AbiState default_abi_state = 0; + +bool use_sret(AbiState *state,jl_value_t *ty) +{ + return false; +} + +void needPassByRef(AbiState *state,jl_value_t *ty, bool *byRef, bool *inReg) +{ + return; +} + +Type *preferred_llvm_type(jl_value_t *ty, bool isret) +{ + return NULL; +} + +bool need_private_copy(jl_value_t *ty, bool byRef) +{ + return false; +} diff --git a/src/abi_win64.cpp b/src/abi_win64.cpp new file mode 100644 index 0000000000000..71cbe92a5dbc8 --- /dev/null +++ b/src/abi_win64.cpp @@ -0,0 +1,68 @@ +//===-- abi_win64.cpp - Windows x86_64 ABI description ----------*- C++ -*-===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the BSD-style LDC license. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// +// +// The ABI implementation used for 64 bit x86 (i.e. x86_64/AMD64/x64) targets +// on Windows. +// +//===----------------------------------------------------------------------===// + + +// Windows only uses the first four registers of either +struct AbiState { + unsigned char int_regs, sse_regs; +}; + +const AbiState default_abi_state = {4,4}; + + +bool use_sret(AbiState *state,jl_value_t *ty) +{ + if(!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) + return false; + size_t size = jl_datatype_size(ty); + bool sret = !(size == 1 || size == 2 || size == 4 || size == 8); // || jl_is_sse(ty) if every implemented + if(sret) + state->int_regs--; + return sret; +} + +void needPassByRef(AbiState *state,jl_value_t *ty, bool *byRef, bool *inReg, bool *byRefAttr) +{ + if(!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) + return; + if ((jl_datatype_t*)ty == jl_float32_type || (jl_datatype_t*)ty == jl_float64_type) { + state->sse_regs--; + return; + } + size_t size = jl_datatype_size(ty); + *byRef = !(size == 1 || size == 2 || size == 4 || size == 8); // but not sse types + *byRefAttr = *byRef; + if(state->int_regs > 0) { + state->int_regs--; //Windows passes these by pointer + //*inReg = true; + } +} + +Type *preferred_llvm_type(jl_value_t *ty, bool isret) +{ + if(!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) + return NULL; + if(jl_is_bitstype(ty)) + return NULL; + size_t size = jl_datatype_size(ty); + if (size == 1 || size == 2 || size == 4 || size == 8) + return T_int64; + return NULL; +} + +// Windows needs all types pased byRef to be passed in caller allocated memory +bool need_private_copy(jl_value_t *ty, bool byRef) +{ + return byRef; +} diff --git a/src/abi_x86.cpp b/src/abi_x86.cpp new file mode 100644 index 0000000000000..a9aeca7936478 --- /dev/null +++ b/src/abi_x86.cpp @@ -0,0 +1,61 @@ +//===-- abi_x86.cpp - x86 ABI description -----------------------*- C++ -*-===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the BSD-style LDC license. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// +// +// The ABI implementation used for 32 bit x86 targets. +// +//===----------------------------------------------------------------------===// + + +typedef bool AbiState; +AbiState default_abi_state = 0; + +inline bool is_complex64(jl_value_t *ty) +{ + return jl_subtype(ty,(jl_value_t*)jl_complex_type,0) && jl_tparam0(ty) == (jl_value_t*)jl_float32_type; +} + +inline bool is_complex128(jl_value_t *ty) +{ + return jl_subtype(ty,(jl_value_t*)jl_complex_type,0) && jl_tparam0(ty) == (jl_value_t*)jl_float64_type; +} + +bool use_sret(AbiState *state,jl_value_t *ty) +{ + if(!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_bitstype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) + return false; + if(is_complex64(ty)) + return false; + return jl_is_structtype(ty); +} + +void needPassByRef(AbiState *state,jl_value_t *ty, bool *byRef, bool *inReg, bool *byRefAttr) +{ + if(!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_bitstype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) + return; + if(jl_is_structtype(ty) && !need_destructure_argument(ty)) + *byRef = true; + *byRefAttr = *byRef; +} + +Type *preferred_llvm_type(jl_value_t *ty, bool isret) +{ + if(!isret) + return NULL; + if(!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_bitstype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) + return NULL; + // special case Complex{Float32} as a return type + if(jl_subtype(ty,(jl_value_t*)jl_complex_type,0) && jl_tparam0(ty) == (jl_value_t*)jl_float32_type) + return T_int64; + return NULL; +} + +bool need_private_copy(jl_value_t *ty, bool byRef) +{ + return false; +} diff --git a/src/abi_x86_64.cpp b/src/abi_x86_64.cpp new file mode 100644 index 0000000000000..b8b8936de9510 --- /dev/null +++ b/src/abi_x86_64.cpp @@ -0,0 +1,192 @@ +//===-- abi_x86_64.cpp - x86_64 ABI description -----------------*- C++ -*-===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the BSD-style LDC license. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// +// +// The ABI implementation used for 64 bit x86 (i.e. x86_64/AMD64/x64) targets. +// +//===----------------------------------------------------------------------===// + + +// used to track the state of the ABI generator during +// code generation +struct AbiState { + unsigned char int_regs, sse_regs; +}; + +const AbiState default_abi_state = {6,8}; + +enum ArgClass { Integer, Sse, SseUp, X87, X87Up, ComplexX87, NoClass, Memory }; + +struct Classification { + bool isMemory; + ArgClass classes[2]; + + Classification() : isMemory(false) { + classes[0] = NoClass; + classes[1] = NoClass; + } + + void addField(unsigned offset, ArgClass cl) { + if (isMemory) + return; + + // Note that we don't need to bother checking if it crosses 8 bytes. + // We don't get here with unaligned fields, and anything that can be + // big enough to cross 8 bytes (cdoubles, reals, structs and arrays) + // is special-cased in classifyType() + int idx = (offset < 8 ? 0 : 1); + + ArgClass nw = merge(classes[idx], cl); + if (nw != classes[idx]) { + classes[idx] = nw; + + if (nw == Memory) { + classes[1-idx] = Memory; + isMemory = true; + } + } + } + + static ArgClass merge(ArgClass accum, ArgClass cl) { + if (accum == cl) + return accum; + if (accum == NoClass) + return cl; + if (cl == NoClass) + return accum; + if (accum == Memory || cl == Memory) + return Memory; + if (accum == Integer || cl == Integer) + return Integer; + if (accum == X87 || accum == X87Up || accum == ComplexX87 || + cl == X87 || cl == X87Up || cl == ComplexX87) + return Memory; + return Sse; + } +}; + +/*else if (ty == jl_float80_type) { //if this is ever added + accum.addField(offset, X87); + accum.addField(offset+8, X87Up); + } else if (ty->ty == Tcomplex80) { + accum.addField(offset, ComplexX87); + // make sure other half knows about it too: + accum.addField(offset+16, ComplexX87); + } */ +void classifyType(Classification& accum, jl_value_t* ty, uint64_t offset) { + if (jl_is_cpointer_type(ty) || jl_is_array_type(ty)) { + accum.addField(offset, Integer); + } else if (jl_is_bitstype(ty) && jl_datatype_size(ty) == 16) { + // Int128 or other 128bit wide INTEGER types + accum.addField(offset, Integer); + accum.addField(offset+8, Integer); + } + // Floating point types + else if (ty == (jl_value_t*)jl_float64_type || ty == (jl_value_t*)jl_float32_type) { + accum.addField(offset, Sse); + } + // Other integer types + else if (jl_is_bitstype(ty)) + { + if(jl_datatype_size(ty) > 8) + jl_error("Bitstype of this size not supported in the C ABI"); + accum.addField(offset,Integer); + } else if (jl_datatype_size(ty) > 16) { + // This isn't creal, yet is > 16 bytes, so pass in memory. + // Must be after creal case but before arrays and structs, + // the other types that can get bigger than 16 bytes + accum.addField(offset, Memory); + } else if (jl_is_structtype(ty)) { + for (int i = 0; i < jl_tuple_len(((jl_datatype_t*)ty)->types); ++i) { + classifyType(accum, jl_tupleref(((jl_datatype_t*)ty)->types,i), offset + jl_field_offset(ty,i)); + } + } else { + jl_error("Unsupported type in C ABI"); + } +} + +Classification classify(jl_value_t* ty) { + Classification cl; + classifyType(cl, ty, 0); + return cl; +} + +bool use_sret(AbiState *state,jl_value_t *ty) +{ + int sret = classify(ty).isMemory; + if(sret) { + assert(state->int_regs>0 && "WTF? No int regs available?"); + state->int_regs--; + } + return sret; +} + +void needPassByRef(AbiState *state,jl_value_t *ty, bool *byRef, bool *inReg, bool *byRefAttr) +{ + Classification cl = classify(ty); + if (cl.isMemory) { + *byRefAttr = *byRef = true; + return; + } + + + // Figure out how many registers we want for this arg: + AbiState wanted = { 0, 0 }; + for (int i = 0 ; i < 2; i++) { + if (cl.classes[i] == Integer) + wanted.int_regs++; + else if (cl.classes[i] == Sse) + wanted.sse_regs++; + } + + if (wanted.int_regs <= state->int_regs && wanted.sse_regs <= state->sse_regs) { + state->int_regs -= wanted.int_regs; + state->sse_regs -= wanted.sse_regs; + *inReg = true; + } + else if (jl_is_structtype(ty)) + { + // spill to memory even though we would ordinarily pass + // it in registers + *byRef = true; + } + *byRefAttr = *byRef; +} + +Type *preferred_llvm_type(jl_value_t *ty, bool isret) +{ + (void) isret; + // no need to rewrite bitstypes or pointers (really only agregates are the problem) + if (!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_bitstype(ty) || jl_is_cpointer_type(ty)) + return NULL; + + int size = jl_datatype_size(ty); + if(!(size == 1 || size == 2 || size == 4 || size == 8)) + return NULL; + + Classification cl = classify(ty); + if (cl.isMemory) + return NULL; + ArgClass c = Classification::merge(cl.classes[0],cl.classes[1]); + Type *target_type = NULL; + + // Make into an aggregate of + if (c == Sse) + target_type = Type::getDoubleTy(jl_LLVMContext); + else if (c == Integer) + target_type = T_int64; + else + assert("Don't know how to rewrite type"); + + return target_type; +} + +bool need_private_copy(jl_value_t *ty, bool isRef) +{ + return false; +} diff --git a/src/alloc.c b/src/alloc.c index 1fba284bd39a0..7543319b181d4 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -463,9 +463,10 @@ jl_lambda_info_t *jl_new_lambda_info(jl_value_t *ast, jl_tuple_t *sparams) li->fptr = &jl_trampoline; li->roots = NULL; li->functionObject = NULL; + li->specFunctionObject = NULL; li->cFunctionObject = NULL; li->functionID = 0; - li->cFunctionID = 0; + li->specFunctionID = 0; li->specTypes = NULL; li->inferred = 0; li->inInference = 0; diff --git a/src/ccall.cpp b/src/ccall.cpp index 50ceb0071626e..009909d3f1411 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -195,8 +195,8 @@ static Value *runtime_sym_lookup(PointerType *funcptype, char *f_lib, char *f_na #endif } - BasicBlock *dlsym_lookup = BasicBlock::Create(getGlobalContext(), "dlsym"), - *ccall_bb = BasicBlock::Create(getGlobalContext(), "ccall"); + BasicBlock *dlsym_lookup = BasicBlock::Create(jl_LLVMContext, "dlsym"), + *ccall_bb = BasicBlock::Create(jl_LLVMContext, "ccall"); builder.CreateCondBr(builder.CreateICmpNE(builder.CreateLoad(llvmgv), initnul), ccall_bb, dlsym_lookup); ctx->f->getBasicBlockList().push_back(dlsym_lookup); @@ -218,21 +218,56 @@ static Value *runtime_sym_lookup(PointerType *funcptype, char *f_lib, char *f_na return builder.CreatePointerCast(llvmf,funcptype); } +// --- ABI Implementations --- +// Partially based on the LDC ABI implementations licensed under the BSD 3-clause license + +#if defined ABI_LLVM +# include "abi_llvm.cpp" +#elif defined _P64 +# if defined _OS_WINDOWS_ +# include "abi_win64.cpp" +# else +# include "abi_x86_64.cpp" +# endif +#else +# include "abi_x86.cpp" +#endif + +Value *llvm_type_rewrite(Value *v, Type *target_type, jl_value_t *ty, bool isret) +{ + if(preferred_llvm_type(ty,isret) == NULL || target_type == NULL || target_type == v->getType()) + return v; + + assert(!v->getType()->isPointerTy()); + + // LLVM doesn't allow us to cast values directly, so + // we need to use this alloca trick + Value *mem = builder.CreateAlloca(target_type); + builder.CreateStore(v,builder.CreateBitCast(mem,v->getType()->getPointerTo())); + return builder.CreateLoad(mem); +} + +// --- argument passing and scratch space utilities --- + static Value *julia_to_native(Type *ty, jl_value_t *jt, Value *jv, - jl_value_t *argex, bool addressOf, + jl_value_t *aty, bool addressOf, + bool byRef, bool inReg, + bool needCopy, int argn, jl_codectx_t *ctx, bool *needStackRestore) { Type *vt = jv->getType(); + + // We're passing any if (ty == jl_pvalue_llvmt) { return boxed(jv,ctx); } - if (ty == vt && !addressOf) { + if (ty == vt && !addressOf && !byRef) { return jv; } if (vt != jl_pvalue_llvmt) { - // argument value is passed unboxed - if (addressOf) { + // argument value is unboxed + if (addressOf || (byRef && inReg)) { if (ty->isPointerTy() && ty->getContainedType(0)==vt) { // pass the address of an alloca'd thing, not a box // since those are immutable. @@ -247,9 +282,30 @@ static Value *julia_to_native(Type *ty, jl_value_t *jt, Value *jv, (vt->isPointerTy() && ty->isPointerTy())) { if (vt->getPrimitiveSizeInBits() == ty->getPrimitiveSizeInBits()) { - return builder.CreateBitCast(jv, ty); + if (!byRef) { + return builder.CreateBitCast(jv, ty); + } + else { + *needStackRestore = true; + Value *mem = builder.CreateAlloca(ty); + builder.CreateStore(jv,builder.CreateBitCast(mem,vt->getPointerTo())); + return mem; + } } } + else if (vt->isStructTy()) + { + if (!byRef) { + return jv; + } + else { + *needStackRestore = true; + Value *mem = builder.CreateAlloca(vt); + builder.CreateStore(jv,mem); + return mem; + } + } + emit_error("ccall: argument type did not match declaration", ctx); } if (jl_is_tuple(jt)) { @@ -258,7 +314,6 @@ static Value *julia_to_native(Type *ty, jl_value_t *jt, Value *jv, 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 (aty != ety && ety != (jl_value_t*)jl_any_type && jt != (jl_value_t*)jl_voidpointer_type) { std::stringstream msg; msg << "ccall argument "; @@ -320,7 +375,6 @@ static Value *julia_to_native(Type *ty, jl_value_t *jt, Value *jv, 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 "; @@ -328,9 +382,20 @@ static Value *julia_to_native(Type *ty, jl_value_t *jt, Value *jv, emit_typecheck(jv, jt, msg.str(), ctx); } Value *p = data_pointer(jv); - return builder.CreateLoad(builder.CreateBitCast(p, - PointerType::get(ty,0)), - false); + Value *pjv = builder.CreatePointerCast(p, PointerType::get(ty,0)); + if (byRef) { + if(!needCopy) + return pjv; + else { + *needStackRestore = true; + Value *mem = builder.CreateAlloca(ty); + builder.CreateMemCpy(mem,pjv,(uint64_t)jl_datatype_size(jt),(uint64_t)((jl_datatype_t*)jt)->alignment); + return mem; + } + } + else { + return builder.CreateLoad(pjv,false); + } } static jl_value_t *jl_signed_type=NULL; @@ -591,8 +656,7 @@ static Value *emit_llvmcall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) make_gcroot(arg, ctx); } #endif - bool mightNeedTempSpace = false; - argvals[i] = julia_to_native(t,tti,arg,argi,false,i,ctx,&mightNeedTempSpace); + argvals[i] = julia_to_native(t, tti, arg, expr_type(argi, ctx), false, false, false, false, i, ctx, NULL); } Function *f; @@ -714,6 +778,197 @@ static Value *mark_or_box_ccall_result(Value *result, jl_value_t *rt_expr, jl_va return mark_julia_type(result, rt); } +#ifdef LLVM33 + typedef AttributeSet attr_type; +#else + typedef AttrListPtr attr_type; +#endif + +static std::string generate_func_sig(Type **lrt, Type **prt, int &sret, + std::vector &fargt, std::vector &fargt_sig, + std::vector &inRegList, + std::vector &byRefList, attr_type &attributes, + jl_value_t *rt, jl_tuple_t *tt) +{ + size_t nargt = jl_tuple_len(tt); + assert(rt && !jl_is_abstract_ref_type(rt)); + +#if LLVM33 + AttrBuilder retattrs; + std::vector paramattrs; +#else + AttrBuilder retattrs; + std::vector paramattrs; + std::vector attrs; +#endif + AbiState abi = default_abi_state; + sret = 0; + + if (type_is_ghost(*lrt)) { + *prt = *lrt = T_void; + } else { + *prt = preferred_llvm_type(rt,true); + if (*prt == NULL) + *prt = *lrt; + + if (jl_is_datatype(rt) && !jl_is_abstracttype(rt) && use_sret(&abi, rt)) { +#if LLVM33 + paramattrs.push_back(AttrBuilder()); + paramattrs[0].clear(); + paramattrs[0].addAttribute(Attribute::StructRet); +#elif LLVM32 + paramattrs.push_back(AttrBuilder()); + paramattrs[0].clear(); + paramattrs[0].addAttribute(Attributes::StructRet); +#else + attrs.push_back(AttributeWithIndex::get(1, Attribute::StructRet)); +#endif + fargt.push_back(PointerType::get(*prt, 0)); + fargt_sig.push_back(PointerType::get(*prt, 0)); + sret = 1; + } + } + + size_t i; + bool current_isVa = false; + for(i = 0; i < nargt; i++) { +#ifdef LLVM32 + paramattrs.push_back(AttrBuilder()); +#endif + jl_value_t *tti = jl_tupleref(tt,i); + + if (jl_is_vararg_type(tti)) { + current_isVa = true; + tti = jl_tparam0(tti); + } + + Type *t = NULL; + if (jl_is_abstract_ref_type(tti)) { + if (jl_is_typevar(jl_tparam0(tti))) + jl_error("ccall: argument type Ref should have an element type, not Ref{T}"); + tti = (jl_value_t*)jl_voidpointer_type; + 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")); + } +#ifdef LLVM33 + Attribute::AttrKind av; +#elif defined(LLVM32) + Attributes::AttrVal av; +#else + Attribute::AttrConst av; +#endif +#if defined(LLVM32) && !defined(LLVM33) + 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; +#endif +#ifdef LLVM32 + paramattrs[i+sret].addAttribute(av); +#else + 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"; + return msg.str(); + } + } + + // Whether the ABI needs us to pass this by ref and/or in registers + // Valid combinations are: + bool byRefAttr = false; + + // Whether or not LLVM wants us to emit a pointer to the data + bool byRef = false; + + // Whether or not to pass this in registers + bool inReg = false; + + if(jl_is_datatype(tti) && !jl_is_abstracttype(tti)) + needPassByRef(&abi, tti, &byRef, &inReg, &byRefAttr); + + // Add the appropriate LLVM parameter attributes + // Note that even though the LLVM argument is called ByVal + // this really means that the thing we're passing is pointing to + // the thing we want to pass by value +#if LLVM33 + if(byRefAttr) + paramattrs[i+sret].addAttribute(Attribute::ByVal); + if(inReg) + paramattrs[i+sret].addAttribute(Attribute::InReg); +#elif LLVM32 + if(byRefAttr) + paramattrs[i+sret].addAttribute(Attributes::ByVal); + if(inReg) + paramattrs[i+sret].addAttribute(Attributes::InReg); +#else + if(byRefAttr) + attrs.push_back(AttributeWithIndex::get(i+sret+1, Attribute::ByVal)); + if(inReg) + attrs.push_back(AttributeWithIndex::get(i+sret+1, Attribute::InReg)); +#endif + + byRefList.push_back(byRef); + inRegList.push_back(inReg); + + fargt.push_back(t); + + Type *pat = preferred_llvm_type(tti,false); + if (pat != NULL) + t = pat; + else if (byRef) + t = PointerType::get(t,0); + + if(!current_isVa) { + fargt_sig.push_back(t); + } + + } + +#ifdef LLVM33 + if(retattrs.hasAttributes()) + attributes = AttributeSet::get(jl_LLVMContext, AttributeSet::ReturnIndex, retattrs); + for(i = 0; i < nargt+sret; ++i) + if(paramattrs[i].hasAttributes()) + attributes = attributes.addAttributes(jl_LLVMContext, i+1, + AttributeSet::get(jl_LLVMContext, i+1, paramattrs[i])); +#elif LLVM32 + if(retattrs.hasAttributes()) + attrs.push_back(AttributeWithIndex::get(0, Attributes::get(jl_LLVMContext, retattrs))); + for(i = 0; i < nargt+sret; ++i) + if(paramattrs[i].hasAttributes()) + attrs.push_back(AttributeWithIndex::get(i+1, Attributes::get(jl_LLVMContext, paramattrs[i]))); + attributes = AttrListPtr::get(jl_LLVMContext, ArrayRef(attrs)); +#else + attributes = AttrListPtr::get(attrs.data(), attrs.size()); +#endif + return ""; +} + + // ccall(pointer, rettype, (argtypes...), args...) static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) { @@ -729,6 +984,8 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) fptr = symarg.fptr; f_name = symarg.f_name; f_lib = symarg.f_lib; + bool isVa = false; + if (f_name == NULL && fptr == NULL && jl_ptr == NULL) { JL_GC_POP(); emit_error("ccall: null function pointer", ctx); @@ -765,6 +1022,7 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) } } } + if (jl_is_tuple(rt)) { std::string msg = "in " + ctx->funcName + ": ccall: missing return type"; @@ -775,10 +1033,15 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) 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."); + 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* } + 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); Type *lrt = julia_struct_to_llvm(rt); if (lrt == NULL) { @@ -804,89 +1067,7 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) JL_TYPECHK(ccall, tuple, at); JL_TYPECHK(ccall, type, at); jl_tuple_t *tt = (jl_tuple_t*)at; - std::vector fargt(0); - std::vector fargt_sig(0); -#if LLVM33 - std::vector paramattrs; -#else - AttrBuilder retattrs; - std::vector paramattrs; - std::vector attrs; -#endif - int sret = 0; - size_t i; - bool isVa = false; - size_t nargt = jl_tuple_len(tt); - - for(i=0; i < nargt; i++) { -#if LLVM32 || LLVM33 - paramattrs.push_back(AttrBuilder()); -#endif - jl_value_t *tti = jl_tupleref(tt,i); - - if (jl_is_vararg_type(tti)) { - isVa = true; - tti = jl_tparam0(tti); - } - 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; -#elif LLVM32 - Attributes::AttrVal av; -#else - 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; -#else - 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); -#else - 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); - } - } - fargt.push_back(t); - if (!isVa) - fargt_sig.push_back(t); - } // check for calling convention specifier CallingConv::ID cc = CallingConv::C; jl_value_t *last = args[nargs]; @@ -910,6 +1091,21 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) } } + // some sanity checking and check whether there's a vararg + size_t i; + size_t nargt = jl_tuple_len(tt); + for(i=0; i < nargt; i++) { + jl_value_t *tti = jl_tupleref(tt,i); + if (jl_is_cpointer_type(tti) && jl_is_typevar(jl_tparam0(tti))) { + JL_GC_POP(); + emit_error("ccall: argument type Ptr should have an element type, Ptr{T}",ctx); + return literal_pointer_val(jl_nothing); + } + if (jl_is_vararg_type(tti)) { + isVa = true; + } + } + if ((!isVa && jl_tuple_len(tt) != (nargs-2)/2) || ( isVa && jl_tuple_len(tt)-1 > (nargs-2)/2)) jl_error("ccall: wrong number of arguments to C function"); @@ -934,15 +1130,19 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) assert(nargt==1); jl_value_t *argi = args[4]; bool addressOf = false; + jl_value_t *tti = jl_tupleref(tt,0); 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; + } else if (jl_is_abstract_ref_type(tti)) { + tti = (jl_value_t*)jl_voidpointer_type; } Value *ary; - Type *largty = fargt[0]; + Type *largty; + if (addressOf) + largty = jl_pvalue_llvmt; + else + largty = julia_struct_to_llvm(jl_tupleref(tt, 0)); if (largty == jl_pvalue_llvmt) { ary = boxed(emit_expr(argi, ctx),ctx); } else { @@ -978,46 +1178,77 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) savespot = &_savespot; } + std::vector fargt(0); + std::vector fargt_sig(0); + std::vector inRegList(0); + std::vector byRefList(0); + attr_type attrs; + Type *prt = NULL; + int sret = 0; + std::string err_msg = generate_func_sig(&lrt, &prt, sret, fargt, fargt_sig, inRegList, byRefList, attrs, rt, tt); + if(!err_msg.empty()) { + JL_GC_POP(); + emit_error(err_msg,ctx); + return literal_pointer_val(jl_nothing); + } + // emit arguments Value **argvals = (Value**) alloca(((nargs-3)/2 + sret)*sizeof(Value*)); - Value *result; + Value *result = NULL; + + // First, if the ABI requires us to provide the space for the return + // argument, allocate the box and store that as the first argument type if (sret) { - assert(jl_is_structtype(rt)); - result = builder.CreateCall( - prepare_call(jlallocobj_func), - ConstantInt::get(T_size, - sizeof(void*)+((jl_datatype_t*)rt)->size)); - //TODO: Fill type pointer fields with C_NULL's - builder.CreateStore( - literal_pointer_val((jl_value_t*)rt), - emit_nthptr_addr(result, (size_t)0)); - argvals[0] = builder.CreateBitCast( - emit_nthptr_addr(result, (size_t)1), - fargt_sig[0]); + result = emit_newsym(rt,1,NULL,ctx); + assert(result != NULL && "Type was not concrete"); + if (!result->getType()->isPointerTy()) { + Value *mem = builder.CreateAlloca(lrt); + builder.CreateStore(result, mem); + result = mem; + argvals[0] = result; + } else { + argvals[0] = builder.CreateBitCast(emit_nthptr_addr(result, (size_t)1), fargt_sig[0]); + } } + + // save argument depth until after we're done emitting arguments int last_depth = ctx->argDepth; + + // number of parameters to the c function bool needStackRestore = false; for(i=4; i < nargs+1; i+=2) { + // Current C function parameter size_t ai = (i-4)/2; + + // Julia (expression) value of current parameter jl_value_t *argi = args[i]; + + // pass the address of the argument rather than the argument itself bool addressOf = false; if (jl_is_expr(argi) && ((jl_expr_t*)argi)->head == amp_sym) { addressOf = true; argi = jl_exprarg(argi,0); } + + // LLVM type of the current parameter Type *largty; + + // Julia type of the current parameter jl_value_t *jargty; if (isVa && ai >= nargt-1) { largty = fargt[nargt-1]; jargty = jl_tparam0(jl_tupleref(tt,nargt-1)); } else { - largty = fargt[ai]; + largty = fargt[sret+ai]; jargty = jl_tupleref(tt,ai); } + Value *arg; bool needroot = false; if (jl_is_abstract_ref_type(jargty)) { + if (addressOf) + emit_error("ccall: & on a Ref{T} argument is invalid", ctx); 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); @@ -1058,8 +1289,11 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) #endif bool nSR=false; - argvals[ai+sret] = julia_to_native(largty, jargty, arg, argi, addressOf, - ai+1, ctx, &nSR); + argvals[ai + sret] = llvm_type_rewrite( + julia_to_native(largty, jargty, arg, expr_type(argi, ctx), addressOf, byRefList[ai], inRegList[ai], + need_private_copy(jargty, byRefList[ai]), ai + 1, ctx, &nSR), + fargt_sig.size() > ai + sret ? fargt_sig[ai + sret] : preferred_llvm_type(jargty, false), + jargty, false); needStackRestore |= nSR; } @@ -1068,7 +1302,7 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) // keep this close to the function call, so that the compiler can // optimize the global pointer load in the common case Value *llvmf; - FunctionType *functype = FunctionType::get(lrt, fargt_sig, isVa); + FunctionType *functype = FunctionType::get(sret?T_void:prt, fargt_sig, isVa); if (jl_ptr != NULL) { null_pointer_check(jl_ptr,ctx); @@ -1120,26 +1354,22 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) instList.push_front((Instruction*)stacksave); } + //llvmf->dump(); + //for (std::vector::iterator it = argvals.begin() ; it != argvals.end(); ++it) + // (*it)->dump(); + // the actual call Value *ret = builder.CreateCall(prepare_call(llvmf), ArrayRef(&argvals[0],(nargs-3)/2+sret)); - - attr_type attributes; -#ifdef LLVM33 - for(i = 0; i < nargt+sret; ++i) - if (paramattrs[i].hasAttributes()) - attributes = attributes.addAttributes(jl_LLVMContext,i+1, - AttributeSet::get(jl_LLVMContext,i+1,paramattrs[i])); +#if LLVM33 + ((CallInst*)ret)->setAttributes(attrs); #elif LLVM32 - for(i = 0; i < nargt+sret; ++i) - if (paramattrs[i].hasAttributes()) - attrs.push_back(AttributeWithIndex::get(i+1, Attributes::get(jl_LLVMContext,paramattrs[i]))); - attributes = AttrListPtr::get(getGlobalContext(), ArrayRef(attrs)); + ((CallInst*)ret)->setAttributes(AttrListPtr::get(jl_LLVMContext, ArrayRef(attrs))); #else attributes = AttrListPtr::get(attrs.data(),attrs.size()); + ((CallInst*)ret)->setAttributes(attributes); #endif - ((CallInst*)ret)->setAttributes(attributes); if (cc != CallingConv::C) ((CallInst*)ret)->setCallingConv(cc); if (!sret) @@ -1160,21 +1390,39 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) } JL_GC_POP(); - if (!sret && lrt == T_void) - return literal_pointer_val((jl_value_t*)jl_nothing); - if (lrt->isStructTy()) { - //jl_printf(JL_STDERR, "ccall rt: %s -> %s\n", f_name, ((jl_tag_type_t*)rt)->name->name->name); - assert(jl_is_structtype(rt)); - Value *strct = - builder.CreateCall(prepare_call(jlallocobj_func), - ConstantInt::get(T_size, - sizeof(void*)+((jl_datatype_t*)rt)->size)); - builder.CreateStore(literal_pointer_val((jl_value_t*)rt), - emit_nthptr_addr(strct, (size_t)0)); - builder.CreateStore(result, - builder.CreateBitCast(emit_nthptr_addr(strct, (size_t)1), - PointerType::get(lrt,0))); - return mark_julia_type(strct, rt); + // Finally we need to box the result into julia type + // However, if we have already created a box for the return + // type because we the ABI required us to pass a pointer (sret), + // then we do not need to do this. + if (!sret) { + if (lrt == T_void) + result = literal_pointer_val((jl_value_t*)jl_nothing); + else if (lrt->isStructTy()) { + //fprintf(stderr, "ccall rt: %s -> %s\n", f_name, ((jl_tag_type_t*)rt)->name->name->name); + assert(jl_is_structtype(rt)); + + Value *newsym = emit_newsym(rt,1,NULL,ctx); + assert(newsym != NULL && "Type was not concrete"); + if (newsym->getType()->isPointerTy()) { + builder.CreateStore(result,builder.CreateBitCast(emit_nthptr_addr(newsym, (size_t)1), prt->getPointerTo())); + result = newsym; + } else if (lrt != prt) { + result = llvm_type_rewrite(result,lrt,rt,true); + } + // otherwise it's fine to pass this by value. Technically we could do alloca/store/load, + // but why should we? + + } else { + if(prt->getPrimitiveSizeInBits() == lrt->getPrimitiveSizeInBits()) { + result = builder.CreateBitCast(result,lrt); + } + else { + assert(0 && "Unimplemented"); //XXX: ? + } + } + } else { + if (result->getType() != jl_pvalue_llvmt) + result = builder.CreateLoad(result); } return mark_or_box_ccall_result(result, args[2], rt, static_rt, ctx); diff --git a/src/cgutils.cpp b/src/cgutils.cpp index d027d84feb6b3..a4f16c5ec368b 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1792,3 +1792,160 @@ static Value* emit_allocobj(size_t static_size) return builder.CreateCall(prepare_call(jlallocobj_func), ConstantInt::get(T_size, static_size)); } + +// if ptr is NULL this emits a write barrier _back_ +static void emit_write_barrier(jl_codectx_t* ctx, Value *parent, Value *ptr) +{ + /* builder.CreateCall2(wbfunc, builder.CreateBitCast(parent, jl_pvalue_llvmt), builder.CreateBitCast(ptr, jl_pvalue_llvmt)); + return;*/ + parent = builder.CreateBitCast(parent, T_psize); + Value* parent_type = builder.CreateLoad(parent); + Value* parent_mark_bits = builder.CreateAnd(parent_type, 1); + + // the branch hint does not seem to make it to the generated code + //builder.CreateCall2(expect_func, parent_marked, ConstantInt::get(T_int1, 0)); + Value* parent_marked = builder.CreateICmpEQ(parent_mark_bits, ConstantInt::get(T_size, 1)); + + BasicBlock* cont = BasicBlock::Create(getGlobalContext(), "cont"); + BasicBlock* barrier_may_trigger = BasicBlock::Create(getGlobalContext(), "wb_may_trigger", ctx->f); + BasicBlock* barrier_trigger = BasicBlock::Create(getGlobalContext(), "wb_trigger", ctx->f); + builder.CreateCondBr(parent_marked, barrier_may_trigger, cont); + + builder.SetInsertPoint(barrier_may_trigger); + Value* ptr_mark_bit = builder.CreateAnd(builder.CreateLoad(builder.CreateBitCast(ptr, T_psize)), 1); + Value* ptr_not_marked = builder.CreateICmpEQ(ptr_mark_bit, ConstantInt::get(T_size, 0)); + builder.CreateCondBr(ptr_not_marked, barrier_trigger, cont); + builder.SetInsertPoint(barrier_trigger); + builder.CreateCall(prepare_call(queuerootfun), builder.CreateBitCast(parent, jl_pvalue_llvmt)); + builder.CreateBr(cont); + ctx->f->getBasicBlockList().push_back(cont); + builder.SetInsertPoint(cont); +} + +static void emit_checked_write_barrier(jl_codectx_t *ctx, Value *parent, Value *ptr) +{ + BasicBlock *cont; + Value *not_null = builder.CreateICmpNE(ptr, V_null); + BasicBlock *if_not_null = BasicBlock::Create(getGlobalContext(), "wb_not_null", ctx->f); + cont = BasicBlock::Create(getGlobalContext(), "cont"); + builder.CreateCondBr(not_null, if_not_null, cont); + builder.SetInsertPoint(if_not_null); + emit_write_barrier(ctx, parent, ptr); + builder.CreateBr(cont); + ctx->f->getBasicBlockList().push_back(cont); + builder.SetInsertPoint(cont); +} + +static void emit_setfield(jl_datatype_t *sty, Value *strct, size_t idx, + Value *rhs, jl_codectx_t *ctx, bool checked, bool wb) +{ + if (sty->mutabl || !checked) { + Value *addr = + builder.CreateGEP(builder.CreateBitCast(strct, T_pint8), + ConstantInt::get(T_size, sty->fields[idx].offset + sizeof(void*))); + jl_value_t *jfty = jl_tupleref(sty->types, idx); + if (sty->fields[idx].isptr) { + rhs = boxed(rhs, ctx); + builder.CreateStore(rhs, + builder.CreateBitCast(addr, jl_ppvalue_llvmt)); + if (wb) emit_checked_write_barrier(ctx, strct, rhs); + } + else { + typed_store(addr, ConstantInt::get(T_size, 0), rhs, jfty, ctx, sty->mutabl ? tbaa_user : tbaa_immut, strct); + } + } + else { + // TODO: better error + emit_error("type is immutable", ctx); + } +} + +static Value *emit_newsym(jl_value_t *ty, size_t nargs, jl_value_t **args, jl_codectx_t *ctx) +{ + assert(jl_is_datatype(ty)); + assert(jl_is_leaf_type(ty)); + assert(nargs>0); + jl_datatype_t *sty = (jl_datatype_t*)ty; + size_t nf = jl_tuple_len(sty->names); + if (nf > 0) { + if (jl_isbits(sty)) { + Type *lt = julia_type_to_llvm(ty); + if (lt == T_void) + return mark_julia_type(UndefValue::get(NoopType),ty); + Value *strct = UndefValue::get(lt); + size_t na = nargs-1 < nf ? nargs-1 : nf; + unsigned idx = 0; + for(size_t i=0; i < na; i++) { + jl_value_t *jtype = jl_tupleref(sty->types,i); + Type *fty = julia_type_to_llvm(jtype); + if (type_is_ghost(fty)) + continue; + Value *fval = emit_unbox(fty, emit_unboxed(args[i+1],ctx), jtype); + if (fty == T_int1) + fval = builder.CreateZExt(fval, T_int8); + strct = builder. + CreateInsertValue(strct, fval, ArrayRef(&idx,1)); + idx++; + } + return mark_julia_type(strct,ty); + } + Value *f1 = NULL; + size_t j = 0; + int fieldStart = ctx->argDepth; + bool needroots = false; + for(size_t i=1; i < nargs; i++) { + needroots |= might_need_root(args[i]); + } + if (nf > 0 && sty->fields[0].isptr && nargs>1) { + // emit first field before allocating struct to save + // a couple store instructions. avoids initializing + // the first field to NULL, and sometimes the GC root + // for the new struct. + Value *fval = emit_expr(args[1],ctx); + f1 = boxed(fval,ctx); + j++; + if (might_need_root(args[1]) || fval->getType() != jl_pvalue_llvmt) + make_gcroot(f1, ctx); + } + Value *strct = emit_allocobj(sizeof(void*)+sty->size); + builder.CreateStore(literal_pointer_val((jl_value_t*)ty), + emit_nthptr_addr(strct, (size_t)0)); + if (f1) { + if (!jl_subtype(expr_type(args[1],ctx), jl_t0(sty->types), 0)) + emit_typecheck(f1, jl_t0(sty->types), "new", ctx); + emit_setfield(sty, strct, 0, f1, ctx, false, false); + ctx->argDepth = fieldStart; + if (nf > 1 && needroots) + make_gcroot(strct, ctx); + } + else if (nf > 0 && needroots) { + make_gcroot(strct, ctx); + } + for(size_t i=j; i < nf; i++) { + if (sty->fields[i].isptr) { + emit_setfield(sty, strct, i, V_null, ctx, false, false); + } + } + for(size_t i=j+1; i < nargs; i++) { + Value *rhs = emit_expr(args[i],ctx); + if (sty->fields[i-1].isptr && rhs->getType() != jl_pvalue_llvmt && + !needroots) { + // if this struct element needs boxing and we haven't rooted + // the struct, root it now. + make_gcroot(strct, ctx); + needroots = true; + } + if (rhs->getType() == jl_pvalue_llvmt) { + if (!jl_subtype(expr_type(args[i],ctx), jl_tupleref(sty->types,i-1), 0)) + emit_typecheck(rhs, jl_tupleref(sty->types,i-1), "new", ctx); + } + emit_setfield(sty, strct, i-1, rhs, ctx, false, false); + } + ctx->argDepth = fieldStart; + return strct; + } + else { + // 0 fields, singleton + return literal_pointer_val(jl_new_struct_uninit((jl_datatype_t*)ty)); + } +} diff --git a/src/codegen.cpp b/src/codegen.cpp index 36015e79bd34b..82ed3b7709920 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -592,9 +592,9 @@ static void jl_rethrow_with_add(const char *fmt, ...) // --- entry point --- //static int n_emit=0; -static Function *emit_function(jl_lambda_info_t *lam, bool cstyle); +static Function *emit_function(jl_lambda_info_t *lam, bool force_specialized, bool cstyle); //static int n_compile=0; -static Function *to_function(jl_lambda_info_t *li, bool cstyle) +static Function *to_function(jl_lambda_info_t *li, bool force_specialized, bool cstyle) { JL_SIGATOMIC_BEGIN(); assert(!li->inInference); @@ -604,13 +604,15 @@ static Function *to_function(jl_lambda_info_t *li, bool cstyle) nested_compile = true; Function *f = NULL; JL_TRY { - f = emit_function(li, cstyle); + f = emit_function(li, force_specialized, cstyle); //jl_printf(JL_STDOUT, "emit %s\n", li->name->name); //n_emit++; } JL_CATCH { li->functionObject = NULL; li->cFunctionObject = NULL; + li->specFunctionObject = NULL; + li->cFunctionObject = NULL; nested_compile = last_n_c; if (old != NULL) { builder.SetInsertPoint(old); @@ -717,6 +719,13 @@ extern "C" void jl_generate_fptr(jl_function_t *f) (void)jl_ExecutionEngine->getFunctionAddress(((Function*)li->cFunctionObject)->getName()); #else (void)jl_ExecutionEngine->getPointerToFunction((Function*)li->cFunctionObject); +#endif + } + if (li->specFunctionObject != NULL) { +#ifdef USE_MCJIT + (void)jl_ExecutionEngine->getFunctionAddress(((Function*)li->specFunctionObject)->getName()); +#else + (void)jl_ExecutionEngine->getPointerToFunction((Function*)li->specFunctionObject); #endif } JL_SIGATOMIC_END(); @@ -724,6 +733,8 @@ extern "C" void jl_generate_fptr(jl_function_t *f) llvmf->deleteBody(); if (li->cFunctionObject != NULL) ((Function*)li->cFunctionObject)->deleteBody(); + if (li->specFunctionObject != NULL) + ((Function*)li->specFunctionObject)->deleteBody(); } } f->fptr = li->fptr; @@ -735,7 +746,7 @@ extern "C" void jl_compile(jl_function_t *f) if (li->functionObject == NULL) { // objective: assign li->functionObject li->inCompile = 1; - (void)to_function(li, false); + (void)to_function(li, false, false); li->inCompile = 0; } } @@ -746,7 +757,7 @@ void jl_cstyle_compile(jl_function_t *f) if (li->cFunctionObject == NULL) { // objective: assign li->cFunctionObject li->inCompile = 1; - (void)to_function(li, true); + (void)to_function(li, true, true); li->inCompile = 0; } } @@ -900,13 +911,13 @@ void *jl_get_llvmf(jl_function_t *f, jl_tuple_t *types, bool getwrapper) } } if (sf->fptr == &jl_trampoline) { - if (!getwrapper && sf->linfo->cFunctionObject != NULL) - llvmf = (Function*)sf->linfo->cFunctionObject; + if (!getwrapper && sf->linfo->specFunctionObject != NULL) + llvmf = (Function*)sf->linfo->specFunctionObject; else llvmf = (Function*)sf->linfo->functionObject; } else { - llvmf = to_function(sf->linfo, false); + llvmf = to_function(sf->linfo, false, false); } return llvmf; } @@ -1514,50 +1525,6 @@ static Value *emit_boxed_rooted(jl_value_t *e, jl_codectx_t *ctx) return v; } -// if ptr is NULL this emits a write barrier _back_ -static void emit_write_barrier(jl_codectx_t* ctx, Value *parent, Value *ptr) -{ - /* builder.CreateCall2(wbfunc, builder.CreateBitCast(parent, jl_pvalue_llvmt), builder.CreateBitCast(ptr, jl_pvalue_llvmt)); - return;*/ - parent = builder.CreateBitCast(parent, T_psize); - Value* parent_type = builder.CreateLoad(parent); - Value* parent_mark_bits = builder.CreateAnd(parent_type, 1); - - // the branch hint does not seem to make it to the generated code - //builder.CreateCall2(expect_func, parent_marked, ConstantInt::get(T_int1, 0)); - Value* parent_marked = builder.CreateICmpEQ(parent_mark_bits, ConstantInt::get(T_size, 1)); - - BasicBlock* cont = BasicBlock::Create(getGlobalContext(), "cont"); - BasicBlock* barrier_may_trigger = BasicBlock::Create(getGlobalContext(), "wb_may_trigger", ctx->f); - BasicBlock* barrier_trigger = BasicBlock::Create(getGlobalContext(), "wb_trigger", ctx->f); - builder.CreateCondBr(parent_marked, barrier_may_trigger, cont); - - builder.SetInsertPoint(barrier_may_trigger); - Value* ptr_mark_bit = builder.CreateAnd(builder.CreateLoad(builder.CreateBitCast(ptr, T_psize)), 1); - Value* ptr_not_marked = builder.CreateICmpEQ(ptr_mark_bit, ConstantInt::get(T_size, 0)); - builder.CreateCondBr(ptr_not_marked, barrier_trigger, cont); - builder.SetInsertPoint(barrier_trigger); - builder.CreateCall(prepare_call(queuerootfun), builder.CreateBitCast(parent, jl_pvalue_llvmt)); - builder.CreateBr(cont); - ctx->f->getBasicBlockList().push_back(cont); - builder.SetInsertPoint(cont); -} - -static void emit_checked_write_barrier(jl_codectx_t *ctx, Value *parent, Value *ptr) -{ - BasicBlock *cont; - Value *not_null = builder.CreateICmpNE(ptr, V_null); - BasicBlock *if_not_null = BasicBlock::Create(getGlobalContext(), "wb_not_null", ctx->f); - cont = BasicBlock::Create(getGlobalContext(), "cont"); - builder.CreateCondBr(not_null, if_not_null, cont); - builder.SetInsertPoint(if_not_null); - emit_write_barrier(ctx, parent, ptr); - builder.CreateBr(cont); - ctx->f->getBasicBlockList().push_back(cont); - builder.SetInsertPoint(cont); -} - - // --- lambda --- static void jl_add_linfo_root(jl_lambda_info_t *li, jl_value_t *val) @@ -1759,30 +1726,6 @@ static Value *emit_getfield(jl_value_t *expr, jl_sym_t *name, jl_codectx_t *ctx) return result; } -static void emit_setfield(jl_datatype_t *sty, Value *strct, size_t idx, - Value *rhs, jl_codectx_t *ctx, bool checked, bool wb) -{ - if (sty->mutabl || !checked) { - Value *addr = - builder.CreateGEP(builder.CreateBitCast(strct, T_pint8), - ConstantInt::get(T_size, sty->fields[idx].offset + sizeof(void*))); - jl_value_t *jfty = jl_tupleref(sty->types, idx); - if (sty->fields[idx].isptr) { - rhs = boxed(rhs, ctx); - builder.CreateStore(rhs, - builder.CreateBitCast(addr, jl_ppvalue_llvmt)); - if (wb) emit_checked_write_barrier(ctx, strct, rhs); - } - else { - typed_store(addr, ConstantInt::get(T_size, 0), rhs, jfty, ctx, sty->mutabl ? tbaa_user : tbaa_immut, strct); - } - } - else { - // TODO: better error - emit_error("type is immutable", ctx); - } -} - // emit code for is (===). rt1 and rt2 are the julia types of the arguments, // arg1 and arg2 are expressions for the arguments if we have them, or NULL, // and varg1 and varg2 are LLVM values for the arguments if we have them. @@ -2534,12 +2477,12 @@ static Value *emit_call_function_object(jl_function_t *f, Value *theF, Value *th jl_codectx_t *ctx) { Value *result; - if (f!=NULL && specialized && f->linfo!=NULL && f->linfo->cFunctionObject!=NULL) { + if (f!=NULL && specialized && f->linfo!=NULL && f->linfo->specFunctionObject!=NULL) { // emit specialized call site - Function *cf = (Function*)f->linfo->cFunctionObject; + Function *cf = (Function*)f->linfo->specFunctionObject; FunctionType *cft = cf->getFunctionType(); size_t nfargs = cft->getNumParams(); - Value **argvals = (Value**)alloca(nfargs*sizeof(Value*)); + Value **argvals = (Value**) alloca(nfargs*sizeof(Value*)); unsigned idx = 0; for(size_t i=0; i < nargs; i++) { Type *at = cft->getParamType(idx); @@ -3244,92 +3187,10 @@ static Value *emit_expr(jl_value_t *expr, jl_codectx_t *ctx, bool isboxed, else if (head == new_sym) { jl_value_t *ty = expr_type(args[0], ctx); size_t nargs = jl_array_len(ex->args); - if (jl_is_type_type(ty) && jl_is_datatype(jl_tparam0(ty)) && + if (jl_is_type_type(ty) && + jl_is_datatype(jl_tparam0(ty)) && jl_is_leaf_type(jl_tparam0(ty))) { - ty = jl_tparam0(ty); - jl_datatype_t *sty = (jl_datatype_t*)ty; - size_t nf = jl_tuple_len(sty->names); - if (nf > 0) { - if (jl_isbits(sty)) { - Type *lt = julia_type_to_llvm(ty); - if (lt == T_void) - return mark_julia_type(UndefValue::get(NoopType),ty); - Value *strct = UndefValue::get(lt); - size_t na = nargs-1 < nf ? nargs-1 : nf; - unsigned idx = 0; - for(size_t i=0; i < na; i++) { - jl_value_t *jtype = jl_tupleref(sty->types,i); - Type *fty = julia_type_to_llvm(jtype); - if (type_is_ghost(fty)) - continue; - Value *fval = emit_unbox(fty, emit_unboxed(args[i+1],ctx), jtype); - if (fty == T_int1) - fval = builder.CreateZExt(fval, T_int8); - strct = builder. - CreateInsertValue(strct, fval, ArrayRef(&idx,1)); - idx++; - } - return mark_julia_type(strct,ty); - } - Value *f1 = NULL; - size_t j = 0; - int fieldStart = ctx->argDepth; - bool needroots = false; - for(size_t i=1; i < nargs; i++) { - needroots |= might_need_root(args[i]); - } - if (nf > 0 && sty->fields[0].isptr && nargs>1) { - // emit first field before allocating struct to save - // a couple store instructions. avoids initializing - // the first field to NULL, and sometimes the GC root - // for the new struct. - Value *fval = emit_expr(args[1],ctx); - f1 = boxed(fval,ctx); - j++; - if (might_need_root(args[1]) || fval->getType() != jl_pvalue_llvmt) - make_gcroot(f1, ctx); - } - Value *strct = emit_allocobj(sizeof(void*)+sty->size); - builder.CreateStore(literal_pointer_val((jl_value_t*)ty), - emit_nthptr_addr(strct, (size_t)0)); - if (f1) { - if (!jl_subtype(expr_type(args[1],ctx), jl_t0(sty->types), 0)) - emit_typecheck(f1, jl_t0(sty->types), "new", ctx); - emit_setfield(sty, strct, 0, f1, ctx, false, false); - ctx->argDepth = fieldStart; - if (nf > 1 && needroots) - make_gcroot(strct, ctx); - } - else if (nf > 0 && needroots) { - make_gcroot(strct, ctx); - } - for(size_t i=j; i < nf; i++) { - if (sty->fields[i].isptr) { - emit_setfield(sty, strct, i, V_null, ctx, false, false); - } - } - for(size_t i=j+1; i < nargs; i++) { - Value *rhs = emit_expr(args[i],ctx); - if (sty->fields[i-1].isptr && rhs->getType() != jl_pvalue_llvmt && - !needroots) { - // if this struct element needs boxing and we haven't rooted - // the struct, root it now. - make_gcroot(strct, ctx); - needroots = true; - } - if (rhs->getType() == jl_pvalue_llvmt) { - if (!jl_subtype(expr_type(args[i],ctx), jl_tupleref(sty->types,i-1), 0)) - emit_typecheck(rhs, jl_tupleref(sty->types,i-1), "new", ctx); - } - emit_setfield(sty, strct, i-1, rhs, ctx, false, false); - } - ctx->argDepth = fieldStart; - return strct; - } - else { - // 0 fields, singleton - return literal_pointer_val(jl_new_struct_uninit((jl_datatype_t*)ty)); - } + return emit_newsym(jl_tparam0(ty),nargs,args,ctx); } Value *typ = emit_expr(args[0], ctx); return emit_jlcall(jlnew_func, typ, &args[1], nargs-1, ctx); @@ -3629,7 +3490,7 @@ static Function *gen_jlcall_wrapper(jl_lambda_info_t *lam, jl_expr_t *ast, Funct { std::stringstream funcName; const std::string &fname = f->getName().str(); - funcName << "jlcall_"; + funcName << "jlapi_"; if (fname.compare(0, 6, "julia_") == 0) funcName << fname.substr(6); else @@ -3694,7 +3555,7 @@ static Function *gen_jlcall_wrapper(jl_lambda_info_t *lam, jl_expr_t *ast, Funct } // cstyle = compile with c-callable signature, not jlcall -static Function *emit_function(jl_lambda_info_t *lam, bool cstyle) +static Function *emit_function(jl_lambda_info_t *lam, bool force_specialized, bool cstyle) { // step 1. unpack AST and allocate codegen context for this function jl_expr_t *ast = (jl_expr_t*)lam->ast; @@ -3823,7 +3684,7 @@ static Function *emit_function(jl_lambda_info_t *lam, bool cstyle) Function *f = NULL; bool specsig = false; - if (cstyle && !va && !hasCapt) { + if (force_specialized && !va && !hasCapt) { specsig = true; } else { @@ -3878,15 +3739,135 @@ static Function *emit_function(jl_lambda_info_t *lam, bool cstyle) imaging_mode ? GlobalVariable::InternalLinkage : GlobalVariable::ExternalLinkage, funcName.str(), m); addComdat(f); - if (lam->cFunctionObject == NULL) { - lam->cFunctionObject = (void*)f; - lam->cFunctionID = jl_assign_functionID(f); + if (lam->specFunctionObject == NULL) { + lam->specFunctionObject = (void*)f; + lam->specFunctionID = jl_assign_functionID(f); } if (lam->functionObject == NULL) { Function *fwrap = gen_jlcall_wrapper(lam, ast, f); lam->functionObject = (void*)fwrap; lam->functionID = jl_assign_functionID(fwrap); } + if (cstyle && lam->cFunctionObject == NULL) { + // Generate a c-callable wrapper + Type *crt = julia_struct_to_llvm(jlrettype); + if (crt == NULL) + jl_error("cfunction: return type doesn't correspond to a C type"); + size_t i; + size_t nargt = jl_tuple_len(lam->specTypes); + for(i=0; i < nargt; i++) { + jl_value_t *tti = jl_tupleref(lam->specTypes,i); + if (tti == (jl_value_t*)jl_pointer_type) { + jl_error("cfunction: argument type Ptr should have an element type, Ptr{T}"); + } + } + + std::vector fargt(0); + std::vector fargt_sig(0); + std::vector inRegList(0); + std::vector byRefList(0); + attr_type attrs; + Type *prt = NULL; + int sret = 0; + std::string err_msg = generate_func_sig(&crt, &prt, sret, fargt, fargt_sig, inRegList, byRefList, attrs, jlrettype, lam->specTypes); + if (!err_msg.empty()) + jl_error(err_msg.c_str()); + + funcName.str(""); + funcName << "jlcapi_" << lam->name->name << "_" << globalUnique; + Function *cw = Function::Create(FunctionType::get(sret ? T_void : prt, fargt_sig, false), + imaging_mode ? GlobalVariable::InternalLinkage : GlobalVariable::ExternalLinkage, + funcName.str(), m); + cw->setAttributes(attrs); + + BasicBlock *b0 = BasicBlock::Create(jl_LLVMContext, "top", cw); + + builder.SetInsertPoint(b0); + DebugLoc noDbg; + builder.SetCurrentDebugLocation(noDbg); + + // Alright, let's do this! + // let's first emit the arguments + size_t nargs = jl_tuple_len(lam->specTypes); + std::vector args; + Function::arg_iterator AI = cw->arg_begin(); + Value *sretPtr = NULL; + if (sret) + sretPtr = AI++; //const Argument &fArg = *AI++; + + for (size_t i=0; i < nargs; i++) { + jl_value_t *jty = jl_tupleref(lam->specTypes,i); + Value *v = AI++; + Value *val = NULL; + if (fargt[i+sret] == fargt_sig[i+sret]) { + val = v; + } + else { + // undo whatever we did to this poor argument + val = llvm_type_rewrite(v, fargt[i+sret], jl_tupleref(lam->specTypes,i), false); + } + // and perhaps box it if necessary + Type *t = julia_type_to_llvm(jty); + if (byRefList[i]) { + val = builder.CreateLoad(val,false); + } + if (t != fargt[i+sret]) { + if (t == jl_pvalue_llvmt) { + if (byRefList[i]) + v = builder.CreateLoad(v,false); + Value *mem = emit_newsym(jty, 1, NULL, &ctx); + if (!mem->getType()->isPointerTy()) { + assert(type_is_ghost(t) && mem->getType() == t); + } + else { + builder.CreateStore(val, builder.CreateBitCast( + emit_nthptr_addr(mem, (size_t)1), val->getType()->getPointerTo())); + } + val = builder.CreateBitCast(mem, jl_pvalue_llvmt); + } + else { + assert(0); + } + } + + args.push_back(val); + } + + Value *r = builder.CreateCall(f, ArrayRef(args)); + + if (type_is_ghost(crt)) { + assert(type_is_ghost(prt)); + builder.CreateRetVoid(); + } + else { + Value *v = julia_to_native(crt, jlrettype, r, jlrettype, 0, false, false, false, 0, &ctx, NULL); + if (!sret) { + builder.CreateRet(llvm_type_rewrite(v, prt, jlrettype, true)); + } + else { + Value *sretVal = llvm_type_rewrite(v, fargt_sig[0], jlrettype, true); + builder.CreateStore(sretVal, sretPtr); + builder.CreateRetVoid(); + } + } + +#ifdef JL_DEBUG_BUILD +#ifdef LLVM35 + llvm::raw_fd_ostream out(1,false); +#endif + if ( +#ifdef LLVM35 + verifyFunction(*cw,&out) +#else + verifyFunction(*cw,PrintMessageAction) +#endif + ) { + cw->dump(); + abort(); + } +#endif + lam->cFunctionObject = (void*)cw; + } } else { f = Function::Create(jl_func_sig, imaging_mode ? GlobalVariable::InternalLinkage : GlobalVariable::ExternalLinkage, @@ -4614,9 +4595,9 @@ extern "C" void jl_fptr_to_llvm(void *fptr, jl_lambda_info_t *lam, int specsig) Function::ExternalLinkage, funcName, jl_Module); #endif - if (lam->cFunctionObject == NULL) { - lam->cFunctionObject = (void*)f; - lam->cFunctionID = jl_assign_functionID(f); + if (lam->specFunctionObject == NULL) { + lam->specFunctionObject = (void*)f; + lam->specFunctionID = jl_assign_functionID(f); } add_named_global(f, (void*)fptr); } diff --git a/src/dump.c b/src/dump.c index 3002abd409369..a5016c5e2c959 100644 --- a/src/dump.c +++ b/src/dump.c @@ -662,7 +662,7 @@ static void jl_serialize_value_(ios_t *s, jl_value_t *v) jl_serialize_value(s, (jl_value_t*)li->unspecialized); // save functionObject pointers write_int32(s, li->functionID); - write_int32(s, li->cFunctionID); + write_int32(s, li->specFunctionID); } else if (jl_typeis(v, jl_module_type)) { jl_serialize_module(s, (jl_module_t*)v); @@ -1122,11 +1122,12 @@ static jl_value_t *jl_deserialize_value_(ios_t *s, jl_value_t *vtag, jl_value_t li->fptr = &jl_trampoline; li->functionObject = NULL; li->cFunctionObject = NULL; + li->specFunctionObject = NULL; li->inInference = 0; li->inCompile = 0; li->unspecialized = (jl_function_t*)jl_deserialize_value(s, (jl_value_t**)&li->unspecialized); li->functionID = 0; - li->cFunctionID = 0; + li->specFunctionID = 0; int32_t cfunc_llvm, func_llvm; func_llvm = read_int32(s); cfunc_llvm = read_int32(s); diff --git a/src/init.c b/src/init.c index 4f0cc08a68091..da2cac2efafbb 100644 --- a/src/init.c +++ b/src/init.c @@ -1284,6 +1284,7 @@ DLLEXPORT void jl_get_system_hooks(void) jl_methoderror_type = (jl_datatype_t*)basemod("MethodError"); jl_loaderror_type = (jl_datatype_t*)basemod("LoadError"); jl_weakref_type = (jl_datatype_t*)basemod("WeakRef"); + jl_complex_type = (jl_datatype_t*)basemod("Complex"); } DLLEXPORT void jl_exit_on_sigint(int on) {exit_on_sigint = on;} diff --git a/src/jltypes.c b/src/jltypes.c index 0de93186e18d2..e212d95dc900a 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -51,6 +51,7 @@ jl_datatype_t *jl_float32_type; jl_datatype_t *jl_float64_type; jl_datatype_t *jl_floatingpoint_type; jl_datatype_t *jl_number_type; +jl_datatype_t *jl_complex_type; jl_tuple_t *jl_null; jl_value_t *jl_nothing; diff --git a/src/julia.h b/src/julia.h index 77faca5e8f145..7f9d83e1538db 100644 --- a/src/julia.h +++ b/src/julia.h @@ -162,11 +162,14 @@ typedef struct _jl_lambda_info_t { // used to avoid infinite recursion int8_t inInference : 1; int8_t inCompile : 1; - jl_fptr_t fptr; // jlcall entry point - void *functionObject; // jlcall llvm Function - void *cFunctionObject; // c callable llvm Function + jl_fptr_t fptr; // jlcall entry point + void *functionObject; // jlcall llvm Function + void *cFunctionObject; // c callable llvm Function + + // specialized llvm Function (common core for the other two) + void *specFunctionObject; int32_t functionID; // index that this function will have in the codegen table - int32_t cFunctionID; // index that this cFunction will have in the codegen table + int32_t specFunctionID; // index that this specFunction will have in the codegen table } jl_lambda_info_t; #define LAMBDA_INFO_NW (NWORDS(sizeof(jl_lambda_info_t))-1) @@ -365,6 +368,7 @@ extern DLLEXPORT jl_datatype_t *jl_float64_type; extern DLLEXPORT jl_datatype_t *jl_floatingpoint_type; extern DLLEXPORT jl_datatype_t *jl_number_type; extern DLLEXPORT jl_datatype_t *jl_void_type; +extern DLLEXPORT jl_datatype_t *jl_complex_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; diff --git a/src/sys.c b/src/sys.c index 7d1ea4f0f9500..cc9145b765738 100644 --- a/src/sys.c +++ b/src/sys.c @@ -544,6 +544,19 @@ DLLEXPORT long jl_SC_CLK_TCK(void) #endif } +DLLEXPORT size_t jl_get_field_offset(jl_datatype_t *ty, int field) +{ + if(field > jl_tuple_len(ty->names)) + jl_error("This type does not have that many fields"); + return ty->fields[field].offset; +} + +DLLEXPORT size_t jl_get_alignment(jl_datatype_t *ty) +{ + return ty->alignment; +} + + // Dynamic Library interrogation #ifdef __APPLE__ diff --git a/test/.gitignore b/test/.gitignore index e382bb3b9d25c..a1af9ae3d44bf 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -1,2 +1,4 @@ /ccall /ccalltest +/ccalltest.s +/libccalltest.* diff --git a/test/Makefile b/test/Makefile index f83b90bffff5b..257b530f03dbf 100644 --- a/test/Makefile +++ b/test/Makefile @@ -15,11 +15,11 @@ clean: @$(MAKE) -C perf $@ -rm -f libccalltest.$(SHLIB_EXT) ccalltest -.PHONY: $(TESTS) perf clean +.PHONY: $(TESTS) perf clean libccalltest +all ccall libccalltest: libccalltest.$(SHLIB_EXT) libccalltest.$(SHLIB_EXT): ccalltest.c - $(CC) $(CFLAGS) $(DEBUGFLAGS) -O3 $< -fPIC -shared -o $@ $(LDFLAGS) -DCC=$(CC) -ccall: libccalltest.$(SHLIB_EXT) + @$(call PRINT_CC, $(CC) $(CFLAGS) $(DEBUGFLAGS) -O3 $< $(fPIC) -shared -o $@ $(LDFLAGS) -DCC=$(CC)) -ccalltest: ccalltest.c - $(CC) $(CFLAGS) $(DEBUGFLAGS) -O3 $< -o $@ $(LDFLAGS) -DCC=$(CC) +ccalltest: ccalltest.c libccalltest.$(SHLIB_EXT) + @$(call PRINT_CC, $(CC) $(CFLAGS) $(DEBUGFLAGS) -O3 $< -o $@ $(LDFLAGS) -DCC=$(CC)) diff --git a/test/ccall.jl b/test/ccall.jl index 67f3be04ade7a..55b93222f3dff 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -1,9 +1,14 @@ +import Base.copy, Base.== +const verbose = false +ccall((:set_verbose, "./libccalltest"), Void, (Int32,), verbose) + +# Test for proper argument register truncation ccall_test_func(x) = ccall((:testUcharX, "./libccalltest"), Int32, (UInt8,), x % UInt8) @test ccall_test_func(3) == 1 @test ccall_test_func(259) == 1 +# 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) - type IntLike x::Int end @@ -18,7 +23,118 @@ end @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 + +# Tests for passing and returning structs +ci = 20+51im +b = ccall((:ctest, "./libccalltest"), Complex{Int}, (Complex{Int},), ci) +@test b == ci + 1 - 2im +b = unsafe_load(ccall((:cptest, "./libccalltest"), Ptr{Complex{Int}}, (Ptr{Complex{Int}},), &ci)) +@test b == ci + 1 - 2im +@test ci == 20+51im + + +cf64 = 2.84+5.2im +b = ccall((:cgtest, "./libccalltest"), Complex128, (Complex128,), cf64) +@test b == cf64 + 1 - 2im +b = unsafe_load(ccall((:cgptest, "./libccalltest"), Ptr{Complex128}, (Ptr{Complex128},), &cf64)) +@test b == cf64 + 1 - 2im +@test cf64 == 2.84+5.2im + +cf32 = 3.34f0+53.2f0im +b = ccall((:cftest, "./libccalltest"), Complex64, (Complex64,), cf32) +@test b == cf32 + 1 - 2im +b = unsafe_load(ccall((:cfptest, "./libccalltest"), Ptr{Complex64}, (Ptr{Complex64},), &cf32)) +@test b == cf32 + 1 - 2im +@test cf32 == 3.34f0+53.2f0im + + +# Tests for native Julia data types +@test_throws MethodError ccall((:cptest, "./libccalltest"), Ptr{Complex{Int}}, (Ptr{Complex{Int}},), cf32) +@test_throws ErrorException ccall((:cptest, "./libccalltest"), Ptr{Complex{Int}}, (Complex{Int},), &cf32) #compile-time error + +# Tests for various sized data types (ByVal) +type Struct1 + x::Float32 + y::Float64 +end +==(a::Struct1,b::Struct1) = a.x == b.x && a.y == b.y +copy(a::Struct1) = Struct1(a.x, a.y) +s1 = Struct1(352.39422f23, 19.287577) +a = copy(s1) +b = ccall((:test_1, "./libccalltest"), Struct1, (Struct1,), a) +@test a.x == s1.x && a.y == s1.y +@test !(a === b) +@test b.x == s1.x + 1 && b.y == s1.y - 2 + +function foos1(s::Struct1) + @test !(s === a) + @test s == a + s +end + +ci32 = Complex{Int32}(int32(10),int32(31)) +ba = ccall((:test_2a, "./libccalltest"), Complex{Int32}, (Complex{Int32},), ci32) +bb = ccall((:test_2b, "./libccalltest"), Complex{Int32}, (Complex{Int32},), ci32) +@test ba == bb == ci32 + 1 - 2im +@test ci32 == Complex{Int32}(int32(10),int32(31)) + +ci64 = Complex{Int64}(int64(20),int64(51)) +ba = ccall((:test_3a, "./libccalltest"), Complex{Int64}, (Complex{Int64},), ci64) +bb = ccall((:test_3b, "./libccalltest"), Complex{Int64}, (Complex{Int64},), ci64) +bc = ccall((:test_128, "./libccalltest"), Complex{Int64}, (Complex{Int64},), ci64) +@test ba == bb == ci64 + 1 - 2im +@test bc == ci64 + 1 +@test ci64 == Complex{Int64}(int64(20),int64(51)) + +i128 = int128(0x7f00123456789abc)<<64 + typemax(UInt64) +b = ccall((:test_128, "./libccalltest"), Int128, (Int128,), i128) +@test b == i128 + 1 +@test i128 == int128(0x7f00123456789abc)<<64 + typemax(UInt64) + +type Struct_Big + x::Int + y::Int + z::Int8 +end +==(a::Struct_Big,b::Struct_Big) = a.x == b.x && a.y == b.y && a.z == b.z +copy(a::Struct_Big) = Struct_Big(a.x, a.y, a.z) +sbig = Struct_Big(424,-5,int8('Z')) + +a = copy(sbig) +b = ccall((:test_big, "./libccalltest"), Struct_Big, (Struct_Big,), a) +@test a.x == sbig.x && a.y == sbig.y && a.z == sbig.z +@test b.x == sbig.x + 1 +@test b.y == sbig.y - 2 +@test b.z == sbig.z - int('A') + +verbose && println("Testing cfunction roundtrip: ") +# cfunction roundtrip +for (t,v) in ((Complex{Int32},:ci32),(Complex{Int64},:ci64), + (Complex64,:cf32),(Complex128,:cf64),(Struct1,:s1)) + @eval begin + verbose && println($t) + if($(t).mutable) + a = copy($v) + else + a = $v + end + verbose && println("A: ",a) + function $(symbol("foo"*string(v)))(s::$t) + verbose && println("B: ",s) + if($(t).mutable) + @test !(s === a) + end + @test s == a + s + end + b = ccall(cfunction($(symbol("foo"*string(v))),$t,($t,)),$t,($t,),$v) + verbose && println("C: ",b) + if($(t).mutable) + @test !(b === a) + end + @test a == b + end +end diff --git a/test/ccalltest.c b/test/ccalltest.c index 802e21713d2c2..93f99c8fede74 100644 --- a/test/ccalltest.c +++ b/test/ccalltest.c @@ -1,7 +1,95 @@ #include +#include +#include +#include +int verbose = 1; + +/******************************************************************************* + * * Compiler * + * *******************************************************************************/ + +/* + * * Notes: + * * + * * 1. Checking for Intel's compiler should be done before checking for + * * Microsoft's. On Windows Intel's compiler also defines _MSC_VER as the + * * acknoledgement of the fact that it is integrated with Visual Studio. + * * + * * 2. Checking for MinGW should be done before checking for GCC as MinGW + * * pretends to be GCC. + * */ +#if defined(__clang__) +#define _COMPILER_CLANG_ +#elif defined(__INTEL_COMPILER) || defined(__ICC) +#define _COMPILER_INTEL_ +#elif defined(__MINGW32__) +#define _COMPILER_MINGW_ +#elif defined(_MSC_VER) +#define _COMPILER_MICROSOFT_ +#elif defined(__GNUC__) +#define _COMPILER_GCC_ +#endif + +/******************************************************************************* + * * OS * + * *******************************************************************************/ + +#if defined(__FreeBSD__) +#define _OS_FREEBSD__ +#elif defined(__linux__) +#define _OS_LINUX_ +#elif defined(_WIN32) || defined(_WIN64) +#define _OS_WINDOWS_ +#elif defined(__APPLE__) && defined(__MACH__) +#define _OS_DARWIN_ +#endif + +/******************************************************************************* + * * Architecture * + * *******************************************************************************/ + +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define _CPU_X86_64_ +#elif defined(i386) || defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(_X86_) +#define _CPU_X86_ +#elif defined(__arm__) || defined(_M_ARM) +#define _CPU_ARM_ +#endif + + +#if defined(_CPU_X86_64_) +# define _P64 +#elif defined(_CPU_X86_) +# define _P32 +#elif defined(_OS_WINDOWS_) +/* Not sure how to determine pointer size on Windows running ARM. */ +# if _WIN64 +# define _P64 +# else +# define _P32 +# endif +#elif defined(_COMPILER_GCC_) +# if __x86_64__ || __ppc64__ +# define _P64 +# else +# define _P32 +# endif +#else +# error pointer size not known for your platform / compiler +#endif + +#ifdef _P64 +#define jint int64_t +#else +#define jint int32_t +#endif + +////////////////////////////////// +// Test for proper argument register truncation int xs[300] = {0,0,0,1,0}; +//int testUcharX(unsigned char x); int __attribute__((noinline)) testUcharX(unsigned char x) { return xs[x]; } @@ -12,6 +100,315 @@ volatile int (*fptr)(unsigned char x); volatile int a; volatile int b; +////////////////////////////////// +// Tests for passing and returning Structs + +// Complex-like data types +typedef struct { + jint real; + jint imag; +} complex_t; + +complex_t ctest(complex_t a) { + a.real += 1; + a.imag -= 2; + return a; +} + +complex double cgtest(complex double a) { + //Unpack a ComplexPair{Float64} struct + if (verbose) printf("%g + %g i\n", creal(a), cimag(a)); + a += 1 - 2i; + return a; +} + +complex double* cgptest(complex double *a) { + //Unpack a ComplexPair{Float64} struct + if (verbose) printf("%g + %g i\n", creal(*a), cimag(*a)); + *a += 1 - 2i; + return a; +} + +complex float cftest(complex float a) { + //Unpack a ComplexPair{Float32} struct + if (verbose) printf("%g + %g i\n", creal(a), cimag(a)); + a += 1 - 2i; + return a; +} + +complex float* cfptest(complex float *a) { + //Unpack a ComplexPair{Float64} struct + if (verbose) printf("%g + %g i\n", creal(*a), cimag(*a)); + *a += 1 - 2i; + return a; +} + +complex_t* cptest(complex_t *a) { + //Unpack a ComplexPair{Int} struct pointer + if (verbose) printf("%lld + %lld i\n", a->real, a->imag); + a->real += 1; + a->imag -= 2; + return a; +} + +// Native-like data types +char* stest(char *x) { + //Print a character Array + if (verbose) printf("%s\n", x); + return x; +} + +struct jl_asciistring_t { struct { void* type; char* data; } *data; }; +char* sptest(struct jl_asciistring_t str) { + //Unpack an ASCIIString + return stest(str.data->data); +} + +// Various sized data types +typedef struct { + float x; + double y; +} struct1; + +typedef struct { + struct { int32_t x; } x; + struct { int32_t y; } y; +} struct2a; + +typedef struct { + int32_t x; + int32_t y; +} struct2b; + +typedef struct { + struct { int64_t x; } x; + struct { int64_t y; } y; +} struct3a; + +typedef struct { + int64_t x; + int64_t y; +} struct3b; + +typedef struct { + int32_t x; + int32_t y; + int32_t z; +} struct4; + +typedef struct { + int32_t x; + int32_t y; + int32_t z; + int32_t a; +} struct5; + +typedef struct { + int64_t x; + int64_t y; + int64_t z; +} struct6; + +typedef struct { + int64_t x; + char y; +} struct7; + +typedef struct { + int32_t x; + char y; +} struct8; + +typedef struct { + int32_t x; + int16_t y; +} struct9; + +typedef struct { + char x; + char y; + char z; + char a; +} struct10; + +typedef struct { + complex float x; +} struct11; + +typedef struct { + complex float x; + complex float y; +} struct12; + +typedef struct { + complex double x; +} struct13; + +typedef struct { + float x; + float y; +} struct14; + +typedef struct { + double x; + double y; +} struct15; + +typedef struct { + jint x; + jint y; + char z; +} struct_big; + +struct1 test_1(struct1 a) { + //Unpack a "small" struct { float, double } + if (verbose) printf("%g + %g i\n", a.x, a.y); + a.x += 1; + a.y -= 2; + return a; +} + +struct1 add_1(struct1 a, struct1 b) { + // Two small structs + struct1 c; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; +} + +struct2a test_2a(struct2a a) { + //Unpack a ComplexPair{Int32} struct + if (verbose) printf("%" PRId32 " + %" PRId32 " i\n", a.x.x, a.y.y); + a.x.x += 1; + a.y.y -= 2; + return a; +} + +struct2b test_2b(struct2b a) { + //Unpack a ComplexPair{Int32} struct + if (verbose) printf("%" PRId32 " + %" PRId32 " i\n", a.x, a.y); + a.x += 1; + a.y -= 2; + return a; +} + +struct3a test_3a(struct3a a) { + //Unpack a ComplexPair{Int64} struct + if (verbose) printf("%" PRId64 " + %" PRId64 " i\n", a.x.x, a.y.y); + a.x.x += 1; + a.y.y -= 2; + return a; +} + +struct3b test_3b(struct3b a) { + //Unpack a ComplexPair{Int64} struct + if (verbose) printf("%" PRId64 " + %" PRId64 " i\n", a.x, a.y); + a.x += 1; + a.y -= 2; + return a; +} + +struct4 test_4(struct4 a) +{ + if (verbose) printf("(%" PRId32 ",%" PRId32 ",%" PRId32 ")\n", a.x, a.y, a.z); + a.x += 1; + a.y -= 2; + a.z += 3; + return a; +} + + +struct5 test_5(struct5 a) +{ + if (verbose) printf("(%" PRId32 ",%" PRId32 ",%" PRId32 ",%" PRId32 ")\n", a.x, a.y, a.z, a.a); + a.x += 1; + a.y -= 2; + a.z += 3; + a.a -= 4; + + return a; +} + + +struct6 test_6(struct6 a) +{ + if (verbose) printf("(%" PRId64 ",%" PRId64 ",%" PRId64 ")\n", a.x, a.y, a.z); + a.x += 1; + a.y -= 2; + a.z += 3; + return a; +} + +struct7 test_7(struct7 a) +{ + if (verbose) printf("(%" PRId64 ",%" PRId8 ")\n", a.x, a.y); + a.x += 1; + a.y -= 2; + return a; +} + +struct8 test_8(struct8 a) +{ + if (verbose) printf("(%" PRId32 ",%" PRId8 ")\n", a.x, a.y); + a.x += 1; + a.y -= 2; + return a; +} + +struct9 test_9(struct9 a) +{ + if (verbose) printf("(%" PRId32 ",%" PRId16 ")\n", a.x, a.y); + a.x += 1; + a.y -= 2; + return a; +} + +struct10 test_10(struct10 a) +{ + if (verbose) printf("(%" PRId8 ",%" PRId8 ",%" PRId8 ",%" PRId8 ")\n", a.x, a.y, a.z, a.a); + a.x += 1; + a.y -= 2; + a.z += 3; + a.a -= 4; + + return a; +} + +struct14 test_14(struct14 a) { + //The C equivalent of a ComplexPair{Float32} struct (but without special complex ABI) + if (verbose) printf("%g + %g i\n", a.x, a.y); + a.x += 1; + a.y -= 2; + return a; +} + +struct15 test_15(struct15 a) { + //The C equivalent of a ComplexPair{Float32} struct (but without special complex ABI) + if (verbose) printf("%g + %g i\n", a.x, a.y); + a.x += 1; + a.y -= 2; + return a; +} + +#define int128_t struct3b +int128_t test_128(int128_t a) { + //Unpack a Int128 + if (verbose) printf("0x%016" PRIx64 "%016" PRIx64 "\n", a.y, a.x); + a.x += 1; + if (a.x == 0) + a.y += 1; + return a; +} + +struct_big test_big(struct_big a) { + //Unpack a "big" struct { int, int, char } + if (verbose) printf("%lld %lld %c\n", a.x, a.y, a.z); + a.x += 1; + a.y -= 2; + a.z -= 'A'; + return a; +} + int main() { printf("all of the following should be 1 except xs[259] = 0"); a = 3; @@ -19,7 +416,15 @@ int main() { fptr = (volatile int (*)(unsigned char x))&testUcharX; if ((((long)fptr)&((long)1)<<32) == 1) fptr = NULL; printf("compiled with: '%s'\nxs[3] = %d\nxs[259] = %d\ntestUcharX(3) = %d\ntestUcharX(%d) = %d\nfptr(3) = %d\nfptr(259) = %d\n", - xstr(CC), xs[a], xs[b], testUcharX(a), b, testUcharX(b), fptr(a), fptr(b)); + xstr(CC), xs[a], xs[b], testUcharX(a), b, testUcharX((unsigned char)b), fptr(a), fptr(b)); + struct1 a = {352.39422e23, 19.287577}; + a = test_1(a); +} + +////////////////////////////////// +// Turn off verbose for automated tests, leave on for debugging +void set_verbose(int level) { + verbose = level; } void *test_echo_p(void *p) { diff --git a/test/choosetests.jl b/test/choosetests.jl index 2956fb7e30d0e..c7d757b38387f 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -18,7 +18,7 @@ function choosetests(choices = []) "arrayops", "subarray", "reduce", "reducedim", "random", "intfuncs", "simdloop", "blas", "fft", "dsp", "sparse", "bitarray", "copy", "math", "fastmath", "functional", - "operators", "path", + "operators", "path", "ccall", "bigint", "sorting", "statistics", "spawn", "backtrace", "priorityqueue", "arpack", "file", "version", "resolve", "pollfd", "mpfr", "broadcast", "complex", "socket", diff --git a/test/math.jl b/test/math.jl index 2f0056345fdc9..82c6e2d126a57 100644 --- a/test/math.jl +++ b/test/math.jl @@ -75,14 +75,13 @@ end @test_approx_eq erfinv(0.84270079294971486934) 1 @test_approx_eq erfcinv(0.15729920705028513066) 1 @test_approx_eq dawson(1) 0.53807950691276841914 -# TODO: complex versions only supported on 64-bit for now -@unix_only if WORD_SIZE==64 - @test_approx_eq erf(1+2im) -0.53664356577856503399-5.0491437034470346695im - @test_approx_eq erfc(1+2im) 1.5366435657785650340+5.0491437034470346695im - @test_approx_eq erfcx(1+2im) 0.14023958136627794370-0.22221344017989910261im - @test_approx_eq erfi(1+2im) -0.011259006028815025076+1.0036063427256517509im - @test_approx_eq dawson(1+2im) -13.388927316482919244-11.828715103889593303im -end + +@test_approx_eq erf(1+2im) -0.53664356577856503399-5.0491437034470346695im +@test_approx_eq erfc(1+2im) 1.5366435657785650340+5.0491437034470346695im +@test_approx_eq erfcx(1+2im) 0.14023958136627794370-0.22221344017989910261im +@test_approx_eq erfi(1+2im) -0.011259006028815025076+1.0036063427256517509im +@test_approx_eq dawson(1+2im) -13.388927316482919244-11.828715103889593303im + for x in logspace(-200, -0.01) @test_approx_eq_eps erf(erfinv(x)) x 1e-12*x @test_approx_eq_eps erf(erfinv(-x)) -x 1e-12*x From 1d6c898722b0bf27b7567c61d163fb8fa3d43c39 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sun, 1 Feb 2015 13:25:10 -0500 Subject: [PATCH 03/14] fix linux calling conventions --- base/linalg/blas.jl | 4 +- src/Makefile | 2 +- src/abi_win64.cpp | 35 ++++--------- src/abi_x86.cpp | 36 ++++++++------ src/abi_x86_64.cpp | 119 +++++++++++++++++++++++++++----------------- src/ccall.cpp | 13 ++--- src/init.c | 1 + src/jltypes.c | 1 + src/julia.h | 1 + test/ccall.jl | 7 ++- 10 files changed, 121 insertions(+), 98 deletions(-) diff --git a/base/linalg/blas.jl b/base/linalg/blas.jl index 516270e6c0bca..6a43d1a080baf 100644 --- a/base/linalg/blas.jl +++ b/base/linalg/blas.jl @@ -118,7 +118,7 @@ for (fname, elty) in ((:cblas_zdotc_sub,:Complex128), # DOUBLE PRECISION DX(*),DY(*) function dotc(n::Integer, DX::Union(Ptr{$elty},DenseArray{$elty}), incx::Integer, DY::Union(Ptr{$elty},DenseArray{$elty}), incy::Integer) result = Array($elty, 1) - ccall(($(blasfunc(fname)), libblas), $elty, + ccall(($(blasfunc(fname)), libblas), Void, (BlasInt, Ptr{$elty}, BlasInt, Ptr{$elty}, BlasInt, Ptr{$elty}), n, DX, incx, DY, incy, result) result[1] @@ -136,7 +136,7 @@ for (fname, elty) in ((:cblas_zdotu_sub,:Complex128), # DOUBLE PRECISION DX(*),DY(*) function dotu(n::Integer, DX::Union(Ptr{$elty},DenseArray{$elty}), incx::Integer, DY::Union(Ptr{$elty},DenseArray{$elty}), incy::Integer) result = Array($elty, 1) - ccall(($(blasfunc(fname)), libblas), $elty, + ccall(($(blasfunc(fname)), libblas), Void, (BlasInt, Ptr{$elty}, BlasInt, Ptr{$elty}, BlasInt, Ptr{$elty}), n, DX, incx, DY, incy, result) result[1] diff --git a/src/Makefile b/src/Makefile index 5304c2532d3cc..136374c6e884f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -72,7 +72,7 @@ $(julia_flisp.boot): julia-parser.scm julia-syntax.scm \ @$(call PRINT_FLISP, $(call spawn,$(BUILDDIR)/flisp/flisp) ./mk_julia_flisp_boot.scm) $(BUILDDIR)/ast.o $(BUILDDIR)/ast.dbg.obj: $(BUILDDIR)/julia_flisp.boot.inc flisp/*.h -$(BUILDDIR)/codegen.o $(BUILDDIR)/codegen.dbg.obj: intrinsics.cpp cgutils.cpp ccall.cpp +$(BUILDDIR)/codegen.o $(BUILDDIR)/codegen.dbg.obj: intrinsics.cpp cgutils.cpp ccall.cpp abi_*.cpp $(BUILDDIR)/builtins.o $(BUILDDIR)/builtins.dbg.obj: table.c $(BUILDDIR)/support/libsupport.a: support/*.h support/*.c diff --git a/src/abi_win64.cpp b/src/abi_win64.cpp index 71cbe92a5dbc8..988bc1ff7a3e9 100644 --- a/src/abi_win64.cpp +++ b/src/abi_win64.cpp @@ -13,51 +13,38 @@ //===----------------------------------------------------------------------===// -// Windows only uses the first four registers of either struct AbiState { - unsigned char int_regs, sse_regs; }; -const AbiState default_abi_state = {4,4}; +const AbiState default_abi_state = {}; -bool use_sret(AbiState *state,jl_value_t *ty) +bool use_sret(AbiState *state, jl_value_t *ty) { if(!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) return false; size_t size = jl_datatype_size(ty); - bool sret = !(size == 1 || size == 2 || size == 4 || size == 8); // || jl_is_sse(ty) if every implemented - if(sret) - state->int_regs--; - return sret; + if (size <= 8) + return false; + return true; } -void needPassByRef(AbiState *state,jl_value_t *ty, bool *byRef, bool *inReg, bool *byRefAttr) +void needPassByRef(AbiState *state, jl_value_t *ty, bool *byRef, bool *inReg, bool *byRefAttr) { if(!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) return; - if ((jl_datatype_t*)ty == jl_float32_type || (jl_datatype_t*)ty == jl_float64_type) { - state->sse_regs--; - return; - } size_t size = jl_datatype_size(ty); - *byRef = !(size == 1 || size == 2 || size == 4 || size == 8); // but not sse types - *byRefAttr = *byRef; - if(state->int_regs > 0) { - state->int_regs--; //Windows passes these by pointer - //*inReg = true; - } + if (size > 8) + *byRefAttr = *byRef = true; } Type *preferred_llvm_type(jl_value_t *ty, bool isret) { - if(!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) - return NULL; - if(jl_is_bitstype(ty)) + if (!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) return NULL; size_t size = jl_datatype_size(ty); - if (size == 1 || size == 2 || size == 4 || size == 8) - return T_int64; + if (size > 0 && size <= 8) + return Type::getIntNTy(getGlobalContext(), size*8); return NULL; } diff --git a/src/abi_x86.cpp b/src/abi_x86.cpp index a9aeca7936478..351e975bb725e 100644 --- a/src/abi_x86.cpp +++ b/src/abi_x86.cpp @@ -17,40 +17,48 @@ AbiState default_abi_state = 0; inline bool is_complex64(jl_value_t *ty) { - return jl_subtype(ty,(jl_value_t*)jl_complex_type,0) && jl_tparam0(ty) == (jl_value_t*)jl_float32_type; + return jl_complex_type != NULL && jl_is_datatype(ty) && + ((jl_datatype_t*)ty)->name == jl_complex_type->name && + jl_tparam0(ty) == (jl_value_t*)jl_float32_type; } inline bool is_complex128(jl_value_t *ty) { - return jl_subtype(ty,(jl_value_t*)jl_complex_type,0) && jl_tparam0(ty) == (jl_value_t*)jl_float64_type; + return jl_complex_type != NULL && jl_is_datatype(ty) && + ((jl_datatype_t*)ty)->name == jl_complex_type->name && + jl_tparam0(ty) == (jl_value_t*)jl_float64_type; } -bool use_sret(AbiState *state,jl_value_t *ty) +bool use_sret(AbiState *state, jl_value_t *ty) { - if(!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_bitstype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) + if (!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) return false; - if(is_complex64(ty)) + int size = jl_datatype_size(ty); + if (size == 0) return false; - return jl_is_structtype(ty); + if (is_complex64(ty) || (jl_is_bitstype(ty) && size <= 8)) + return false; + return true; } -void needPassByRef(AbiState *state,jl_value_t *ty, bool *byRef, bool *inReg, bool *byRefAttr) +void needPassByRef(AbiState *state, jl_value_t *ty, bool *byRef, bool *inReg, bool *byRefAttr) { - if(!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_bitstype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) + if (!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) + return; + int size = jl_datatype_size(ty); + if (is_complex64(ty) || is_complex128(ty) || (jl_is_bitstype(ty) && size <= 8)) return; - if(jl_is_structtype(ty) && !need_destructure_argument(ty)) - *byRef = true; - *byRefAttr = *byRef; + *byRefAttr = *byRef = true; } Type *preferred_llvm_type(jl_value_t *ty, bool isret) { - if(!isret) + if (!isret) return NULL; - if(!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_bitstype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) + if (!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) return NULL; // special case Complex{Float32} as a return type - if(jl_subtype(ty,(jl_value_t*)jl_complex_type,0) && jl_tparam0(ty) == (jl_value_t*)jl_float32_type) + if (is_complex64(ty)) return T_int64; return NULL; } diff --git a/src/abi_x86_64.cpp b/src/abi_x86_64.cpp index b8b8936de9510..220e4b3add5a0 100644 --- a/src/abi_x86_64.cpp +++ b/src/abi_x86_64.cpp @@ -73,40 +73,46 @@ struct Classification { /*else if (ty == jl_float80_type) { //if this is ever added accum.addField(offset, X87); accum.addField(offset+8, X87Up); - } else if (ty->ty == Tcomplex80) { + } else if (ty->ty == jl_complex80_type) { accum.addField(offset, ComplexX87); // make sure other half knows about it too: accum.addField(offset+16, ComplexX87); } */ void classifyType(Classification& accum, jl_value_t* ty, uint64_t offset) { - if (jl_is_cpointer_type(ty) || jl_is_array_type(ty)) { - accum.addField(offset, Integer); - } else if (jl_is_bitstype(ty) && jl_datatype_size(ty) == 16) { - // Int128 or other 128bit wide INTEGER types - accum.addField(offset, Integer); - accum.addField(offset+8, Integer); - } // Floating point types - else if (ty == (jl_value_t*)jl_float64_type || ty == (jl_value_t*)jl_float32_type) { + if (ty == (jl_value_t*)jl_float64_type || ty == (jl_value_t*)jl_float32_type) { accum.addField(offset, Sse); } - // Other integer types - else if (jl_is_bitstype(ty)) - { - if(jl_datatype_size(ty) > 8) - jl_error("Bitstype of this size not supported in the C ABI"); - accum.addField(offset,Integer); - } else if (jl_datatype_size(ty) > 16) { - // This isn't creal, yet is > 16 bytes, so pass in memory. - // Must be after creal case but before arrays and structs, - // the other types that can get bigger than 16 bytes - accum.addField(offset, Memory); - } else if (jl_is_structtype(ty)) { - for (int i = 0; i < jl_tuple_len(((jl_datatype_t*)ty)->types); ++i) { + // Misc types + else if (!jl_is_datatype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty) || jl_is_abstracttype(ty)) { + accum.addField(offset, Integer); // passed as a pointer + } + // Ghost + else if (jl_datatype_size(ty) == 0) { + } + // BitsTypes and not float, write as Integers + else if (jl_is_bitstype(ty)) { + if (jl_datatype_size(ty) <= 8) { + accum.addField(offset,Integer); + } + else if (jl_datatype_size(ty) <= 16) { + // Int128 or other 128bit wide INTEGER types + accum.addField(offset, Integer); + accum.addField(offset+8, Integer); + } + else { + accum.addField(offset, Memory); + } + } + // Other struct types + else if (jl_datatype_size(ty) <= 16) { + size_t i; + for (i = 0; i < jl_tuple_len(((jl_datatype_t*)ty)->types); ++i) { classifyType(accum, jl_tupleref(((jl_datatype_t*)ty)->types,i), offset + jl_field_offset(ty,i)); } - } else { - jl_error("Unsupported type in C ABI"); + } + else { + accum.addField(offset, Memory); } } @@ -119,14 +125,14 @@ Classification classify(jl_value_t* ty) { bool use_sret(AbiState *state,jl_value_t *ty) { int sret = classify(ty).isMemory; - if(sret) { - assert(state->int_regs>0 && "WTF? No int regs available?"); + if (sret) { + assert(state->int_regs>0 && "No int regs available when determining sret-ness?"); state->int_regs--; } return sret; } -void needPassByRef(AbiState *state,jl_value_t *ty, bool *byRef, bool *inReg, bool *byRefAttr) +void needPassByRef(AbiState *state, jl_value_t *ty, bool *byRef, bool *inReg, bool *byRefAttr) { Classification cl = classify(ty); if (cl.isMemory) { @@ -134,7 +140,6 @@ void needPassByRef(AbiState *state,jl_value_t *ty, bool *byRef, bool *inReg, boo return; } - // Figure out how many registers we want for this arg: AbiState wanted = { 0, 0 }; for (int i = 0 ; i < 2; i++) { @@ -149,41 +154,61 @@ void needPassByRef(AbiState *state,jl_value_t *ty, bool *byRef, bool *inReg, boo state->sse_regs -= wanted.sse_regs; *inReg = true; } - else if (jl_is_structtype(ty)) - { + else if (jl_is_structtype(ty)) { // spill to memory even though we would ordinarily pass // it in registers - *byRef = true; + *byRefAttr = *byRef = true; } - *byRefAttr = *byRef; } Type *preferred_llvm_type(jl_value_t *ty, bool isret) { (void) isret; - // no need to rewrite bitstypes or pointers (really only agregates are the problem) - if (!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_bitstype(ty) || jl_is_cpointer_type(ty)) + // no need to rewrite these types (they are returned as pointers anyways) + if (!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) return NULL; int size = jl_datatype_size(ty); - if(!(size == 1 || size == 2 || size == 4 || size == 8)) + if (size > 16 || size == 0) return NULL; Classification cl = classify(ty); if (cl.isMemory) return NULL; - ArgClass c = Classification::merge(cl.classes[0],cl.classes[1]); - Type *target_type = NULL; - - // Make into an aggregate of - if (c == Sse) - target_type = Type::getDoubleTy(jl_LLVMContext); - else if (c == Integer) - target_type = T_int64; - else - assert("Don't know how to rewrite type"); - - return target_type; + + Type *types[2]; + switch (cl.classes[0]) { + case Integer: + if (size >= 8) + types[0] = T_int64; + else + types[0] = Type::getIntNTy(getGlobalContext(), size*8); + break; + case Sse: + if (size <= 4) + types[0] = T_float32; + else + types[0] = T_float64; + break; + default: + assert(0 && "Unexpected cl.classes[0]"); + } + switch (cl.classes[1]) { + case NoClass: + return types[0]; + case Integer: + assert(size > 8); + types[1] = Type::getIntNTy(getGlobalContext(), (size-8)*8); + return StructType::get(jl_LLVMContext,ArrayRef(&types[0],2)); + case Sse: + if (size <= 12) + types[1] = T_float32; + else + types[1] = T_float64; + return StructType::get(jl_LLVMContext,ArrayRef(&types[0],2)); + default: + assert(0 && "Unexpected cl.classes[0]"); + } } bool need_private_copy(jl_value_t *ty, bool isRef) diff --git a/src/ccall.cpp b/src/ccall.cpp index 009909d3f1411..728096eedb21c 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -398,8 +398,6 @@ static Value *julia_to_native(Type *ty, jl_value_t *jt, Value *jv, } } -static jl_value_t *jl_signed_type=NULL; - typedef struct { Value *jl_ptr; // if the argument is a run-time computed pointer void *fptr; // if the argument is a constant pointer @@ -857,9 +855,6 @@ static std::string generate_func_sig(Type **lrt, Type **prt, int &sret, // 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")); - } #ifdef LLVM33 Attribute::AttrKind av; #elif defined(LLVM32) @@ -868,12 +863,12 @@ static std::string generate_func_sig(Type **lrt, Type **prt, int &sret, Attribute::AttrConst av; #endif #if defined(LLVM32) && !defined(LLVM33) - if (jl_signed_type && jl_subtype(tti, jl_signed_type, 0)) + if (jl_signed_type && jl_subtype(tti, (jl_value_t*)jl_signed_type, 0)) av = Attributes::SExt; else av = Attributes::ZExt; #else - if (jl_signed_type && jl_subtype(tti, jl_signed_type, 0)) + if (jl_signed_type && jl_subtype(tti, (jl_value_t*)jl_signed_type, 0)) av = Attribute::SExt; else av = Attribute::ZExt; @@ -1417,7 +1412,9 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) result = builder.CreateBitCast(result,lrt); } else { - assert(0 && "Unimplemented"); //XXX: ? + Value *rloc = builder.CreateAlloca(prt); + builder.CreateStore(result, rloc); + result = builder.CreateLoad(builder.CreatePointerCast(rloc, PointerType::get(lrt,0))); } } } else { diff --git a/src/init.c b/src/init.c index da2cac2efafbb..02c3a2d2fc5c4 100644 --- a/src/init.c +++ b/src/init.c @@ -1268,6 +1268,7 @@ void jl_get_builtin_hooks(void) jl_utf8_string_type = (jl_datatype_t*)core("UTF8String"); jl_symbolnode_type = (jl_datatype_t*)core("SymbolNode"); jl_globalref_type = (jl_datatype_t*)core("GlobalRef"); + jl_signed_type = (jl_datatype_t*)core("Signed"); jl_array_uint8_type = jl_apply_type((jl_value_t*)jl_array_type, jl_tuple2(jl_uint8_type, diff --git a/src/jltypes.c b/src/jltypes.c index e212d95dc900a..43541e49166c6 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -52,6 +52,7 @@ jl_datatype_t *jl_float64_type; jl_datatype_t *jl_floatingpoint_type; jl_datatype_t *jl_number_type; jl_datatype_t *jl_complex_type; +jl_datatype_t *jl_signed_type; jl_tuple_t *jl_null; jl_value_t *jl_nothing; diff --git a/src/julia.h b/src/julia.h index 7f9d83e1538db..b99b23fd3d9a8 100644 --- a/src/julia.h +++ b/src/julia.h @@ -369,6 +369,7 @@ extern DLLEXPORT jl_datatype_t *jl_floatingpoint_type; extern DLLEXPORT jl_datatype_t *jl_number_type; extern DLLEXPORT jl_datatype_t *jl_void_type; extern DLLEXPORT jl_datatype_t *jl_complex_type; +extern DLLEXPORT jl_datatype_t *jl_signed_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; diff --git a/test/ccall.jl b/test/ccall.jl index 55b93222f3dff..7ce08f03fe679 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -17,7 +17,10 @@ end @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 +if WORD_SIZE == 64 + # this test is valid only for x86_64 and win64 + @test ccall_echo_func(164, IntLike, Int).x === 164 +end @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 @@ -132,7 +135,7 @@ for (t,v) in ((Complex{Int32},:ci32),(Complex{Int64},:ci64), end b = ccall(cfunction($(symbol("foo"*string(v))),$t,($t,)),$t,($t,),$v) verbose && println("C: ",b) - if($(t).mutable) + if ($(t).mutable) @test !(b === a) end @test a == b From 99899a0edf6ab2f33531df27beb67e84949e6989 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 2 Feb 2015 00:08:50 -0500 Subject: [PATCH 04/14] fix win32 and win64 calling conventions --- src/abi_win32.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++++ src/abi_win64.cpp | 2 +- src/abi_x86.cpp | 4 ++-- src/ccall.cpp | 15 ++++++++++---- test/ccall.jl | 1 + test/ccalltest.c | 9 ++++---- 6 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 src/abi_win32.cpp diff --git a/src/abi_win32.cpp b/src/abi_win32.cpp new file mode 100644 index 0000000000000..0899638b74304 --- /dev/null +++ b/src/abi_win32.cpp @@ -0,0 +1,53 @@ +//===-- abi_win32.cpp - x86 ABI description ---------------------*- C++ -*-===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the BSD-style LDC license. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// +// +// The ABI implementation used for 32 bit x86 targets on Windows. +// +//===----------------------------------------------------------------------===// + + +typedef bool AbiState; +AbiState default_abi_state = 0; + +bool use_sret(AbiState *state, jl_value_t *ty) +{ + if (!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) + return false; + size_t size = jl_datatype_size(ty); + if (size <= 8) + return false; + return true; +} + +void needPassByRef(AbiState *state, jl_value_t *ty, bool *byRef, bool *inReg, bool *byRefAttr) +{ + if (!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) + return; + size_t size = jl_datatype_size(ty); + if (size <= 8) + return; + *byRefAttr = *byRef = true; +} + +Type *preferred_llvm_type(jl_value_t *ty, bool isret) +{ + if (!isret) + return NULL; + if (!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) + return NULL; + size_t size = jl_datatype_size(ty); + if (size > 0 && size <= 8 && !jl_is_bitstype(ty)) + return Type::getIntNTy(getGlobalContext(), size*8); + return NULL; +} + +bool need_private_copy(jl_value_t *ty, bool byRef) +{ + return false; +} diff --git a/src/abi_win64.cpp b/src/abi_win64.cpp index 988bc1ff7a3e9..a586e609cda98 100644 --- a/src/abi_win64.cpp +++ b/src/abi_win64.cpp @@ -43,7 +43,7 @@ Type *preferred_llvm_type(jl_value_t *ty, bool isret) if (!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) return NULL; size_t size = jl_datatype_size(ty); - if (size > 0 && size <= 8) + if (size > 0 && size <= 8 && !jl_is_bitstype(ty)) return Type::getIntNTy(getGlobalContext(), size*8); return NULL; } diff --git a/src/abi_x86.cpp b/src/abi_x86.cpp index 351e975bb725e..49563b7e7a759 100644 --- a/src/abi_x86.cpp +++ b/src/abi_x86.cpp @@ -33,7 +33,7 @@ bool use_sret(AbiState *state, jl_value_t *ty) { if (!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) return false; - int size = jl_datatype_size(ty); + size_t size = jl_datatype_size(ty); if (size == 0) return false; if (is_complex64(ty) || (jl_is_bitstype(ty) && size <= 8)) @@ -45,7 +45,7 @@ void needPassByRef(AbiState *state, jl_value_t *ty, bool *byRef, bool *inReg, bo { if (!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_cpointer_type(ty) || jl_is_array_type(ty)) return; - int size = jl_datatype_size(ty); + size_t size = jl_datatype_size(ty); if (is_complex64(ty) || is_complex128(ty) || (jl_is_bitstype(ty) && size <= 8)) return; *byRefAttr = *byRef = true; diff --git a/src/ccall.cpp b/src/ccall.cpp index 728096eedb21c..920fcf5f0fcf9 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -230,7 +230,11 @@ static Value *runtime_sym_lookup(PointerType *funcptype, char *f_lib, char *f_na # include "abi_x86_64.cpp" # endif #else -# include "abi_x86.cpp" +# if defined _OS_WINDOWS_ +# include "abi_win32.cpp" +# else +# include "abi_x86.cpp" +# endif #endif Value *llvm_type_rewrite(Value *v, Type *target_type, jl_value_t *ty, bool isret) @@ -1107,7 +1111,8 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) // some special functions if (fptr == (void *) &jl_array_ptr || - (f_lib==NULL && f_name && !strcmp(f_name,"jl_array_ptr"))) { + ((f_lib==NULL || (intptr_t)f_lib==2) + && f_name && !strcmp(f_name,"jl_array_ptr"))) { assert(lrt->isPointerTy()); assert(!isVa); assert(nargt==1); @@ -1119,7 +1124,8 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) args[2], rt, static_rt, ctx); } if (fptr == (void *) &jl_value_ptr || - (f_lib==NULL && f_name && !strcmp(f_name,"jl_value_ptr"))) { + ((f_lib==NULL || (intptr_t)f_lib==2) + && f_name && !strcmp(f_name,"jl_value_ptr"))) { assert(lrt->isPointerTy()); assert(!isVa); assert(nargt==1); @@ -1149,7 +1155,8 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) args[2], rt, static_rt, ctx); } if (fptr == (void *) &jl_is_leaf_type || - (f_lib==NULL && f_name && !strcmp(f_name, "jl_is_leaf_type"))) { + ((f_lib==NULL || (intptr_t)f_lib==2) + && f_name && !strcmp(f_name, "jl_is_leaf_type"))) { jl_value_t *arg = args[4]; jl_value_t *ty = expr_type(arg, ctx); if (jl_is_type_type(ty) && !jl_is_typevar(jl_tparam0(ty))) { diff --git a/test/ccall.jl b/test/ccall.jl index 7ce08f03fe679..5907b279fe5d0 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -113,6 +113,7 @@ b = ccall((:test_big, "./libccalltest"), Struct_Big, (Struct_Big,), a) @test b.y == sbig.y - 2 @test b.z == sbig.z - int('A') +verbose && flush_cstdio() verbose && println("Testing cfunction roundtrip: ") # cfunction roundtrip for (t,v) in ((Complex{Int32},:ci32),(Complex{Int64},:ci64), diff --git a/test/ccalltest.c b/test/ccalltest.c index 93f99c8fede74..c0408025232ea 100644 --- a/test/ccalltest.c +++ b/test/ccalltest.c @@ -145,7 +145,7 @@ complex float* cfptest(complex float *a) { complex_t* cptest(complex_t *a) { //Unpack a ComplexPair{Int} struct pointer - if (verbose) printf("%lld + %lld i\n", a->real, a->imag); + if (verbose) printf("%lld + %lld i\n", (long long)a->real, (long long)a->imag); a->real += 1; a->imag -= 2; return a; @@ -402,7 +402,7 @@ int128_t test_128(int128_t a) { struct_big test_big(struct_big a) { //Unpack a "big" struct { int, int, char } - if (verbose) printf("%lld %lld %c\n", a.x, a.y, a.z); + if (verbose) printf("%lld %lld %c\n", (long long)a.x, (long long)a.y, a.z); a.x += 1; a.y -= 2; a.z -= 'A'; @@ -410,13 +410,14 @@ struct_big test_big(struct_big a) { } int main() { - printf("all of the following should be 1 except xs[259] = 0"); + printf("all of the following should be 1 except xs[259] = 0\n"); a = 3; b = 259; fptr = (volatile int (*)(unsigned char x))&testUcharX; - if ((((long)fptr)&((long)1)<<32) == 1) fptr = NULL; + if (((size_t)fptr)&((size_t)1) == 1) fptr = NULL; printf("compiled with: '%s'\nxs[3] = %d\nxs[259] = %d\ntestUcharX(3) = %d\ntestUcharX(%d) = %d\nfptr(3) = %d\nfptr(259) = %d\n", xstr(CC), xs[a], xs[b], testUcharX(a), b, testUcharX((unsigned char)b), fptr(a), fptr(b)); + printf("misc tests:\n"); struct1 a = {352.39422e23, 19.287577}; a = test_1(a); } From 892b3d6a0159780b125b51fabb37afb5f69012f2 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sun, 8 Mar 2015 15:57:25 -0400 Subject: [PATCH 05/14] workaround a llvm33/mingw32 calling convention mismatch --- src/ccall.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ccall.cpp b/src/ccall.cpp index 920fcf5f0fcf9..8908d1cb14571 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -817,7 +817,9 @@ static std::string generate_func_sig(Type **lrt, Type **prt, int &sret, #if LLVM33 paramattrs.push_back(AttrBuilder()); paramattrs[0].clear(); +#if !defined(_OS_WINDOWS_) || defined(LLVM35) paramattrs[0].addAttribute(Attribute::StructRet); +#endif #elif LLVM32 paramattrs.push_back(AttrBuilder()); paramattrs[0].clear(); From 67a845b0e01951b756531a2560980b1f8142c6ca Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 7 Mar 2015 18:17:31 -0500 Subject: [PATCH 06/14] print to stderr during ccalltest this makes it possible to see how far the test got if it fails partway through. this also is better under certain combinations of windows consoles at actually printing the output. --- test/ccalltest.c | 52 ++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/test/ccalltest.c b/test/ccalltest.c index c0408025232ea..0b6155fba92fd 100644 --- a/test/ccalltest.c +++ b/test/ccalltest.c @@ -117,35 +117,35 @@ complex_t ctest(complex_t a) { complex double cgtest(complex double a) { //Unpack a ComplexPair{Float64} struct - if (verbose) printf("%g + %g i\n", creal(a), cimag(a)); + if (verbose) fprintf(stderr,"%g + %g i\n", creal(a), cimag(a)); a += 1 - 2i; return a; } complex double* cgptest(complex double *a) { //Unpack a ComplexPair{Float64} struct - if (verbose) printf("%g + %g i\n", creal(*a), cimag(*a)); + if (verbose) fprintf(stderr,"%g + %g i\n", creal(*a), cimag(*a)); *a += 1 - 2i; return a; } complex float cftest(complex float a) { //Unpack a ComplexPair{Float32} struct - if (verbose) printf("%g + %g i\n", creal(a), cimag(a)); + if (verbose) fprintf(stderr,"%g + %g i\n", creal(a), cimag(a)); a += 1 - 2i; return a; } complex float* cfptest(complex float *a) { //Unpack a ComplexPair{Float64} struct - if (verbose) printf("%g + %g i\n", creal(*a), cimag(*a)); + if (verbose) fprintf(stderr,"%g + %g i\n", creal(*a), cimag(*a)); *a += 1 - 2i; return a; } complex_t* cptest(complex_t *a) { //Unpack a ComplexPair{Int} struct pointer - if (verbose) printf("%lld + %lld i\n", (long long)a->real, (long long)a->imag); + if (verbose) fprintf(stderr,"%lld + %lld i\n", (long long)a->real, (long long)a->imag); a->real += 1; a->imag -= 2; return a; @@ -154,7 +154,7 @@ complex_t* cptest(complex_t *a) { // Native-like data types char* stest(char *x) { //Print a character Array - if (verbose) printf("%s\n", x); + if (verbose) fprintf(stderr,"%s\n", x); return x; } @@ -262,7 +262,7 @@ typedef struct { struct1 test_1(struct1 a) { //Unpack a "small" struct { float, double } - if (verbose) printf("%g + %g i\n", a.x, a.y); + if (verbose) fprintf(stderr,"%g + %g i\n", a.x, a.y); a.x += 1; a.y -= 2; return a; @@ -278,7 +278,7 @@ struct1 add_1(struct1 a, struct1 b) { struct2a test_2a(struct2a a) { //Unpack a ComplexPair{Int32} struct - if (verbose) printf("%" PRId32 " + %" PRId32 " i\n", a.x.x, a.y.y); + if (verbose) fprintf(stderr,"%" PRId32 " + %" PRId32 " i\n", a.x.x, a.y.y); a.x.x += 1; a.y.y -= 2; return a; @@ -286,7 +286,7 @@ struct2a test_2a(struct2a a) { struct2b test_2b(struct2b a) { //Unpack a ComplexPair{Int32} struct - if (verbose) printf("%" PRId32 " + %" PRId32 " i\n", a.x, a.y); + if (verbose) fprintf(stderr,"%" PRId32 " + %" PRId32 " i\n", a.x, a.y); a.x += 1; a.y -= 2; return a; @@ -294,7 +294,7 @@ struct2b test_2b(struct2b a) { struct3a test_3a(struct3a a) { //Unpack a ComplexPair{Int64} struct - if (verbose) printf("%" PRId64 " + %" PRId64 " i\n", a.x.x, a.y.y); + if (verbose) fprintf(stderr,"%" PRId64 " + %" PRId64 " i\n", a.x.x, a.y.y); a.x.x += 1; a.y.y -= 2; return a; @@ -302,7 +302,7 @@ struct3a test_3a(struct3a a) { struct3b test_3b(struct3b a) { //Unpack a ComplexPair{Int64} struct - if (verbose) printf("%" PRId64 " + %" PRId64 " i\n", a.x, a.y); + if (verbose) fprintf(stderr,"%" PRId64 " + %" PRId64 " i\n", a.x, a.y); a.x += 1; a.y -= 2; return a; @@ -310,7 +310,7 @@ struct3b test_3b(struct3b a) { struct4 test_4(struct4 a) { - if (verbose) printf("(%" PRId32 ",%" PRId32 ",%" PRId32 ")\n", a.x, a.y, a.z); + if (verbose) fprintf(stderr,"(%" PRId32 ",%" PRId32 ",%" PRId32 ")\n", a.x, a.y, a.z); a.x += 1; a.y -= 2; a.z += 3; @@ -320,7 +320,7 @@ struct4 test_4(struct4 a) struct5 test_5(struct5 a) { - if (verbose) printf("(%" PRId32 ",%" PRId32 ",%" PRId32 ",%" PRId32 ")\n", a.x, a.y, a.z, a.a); + if (verbose) fprintf(stderr,"(%" PRId32 ",%" PRId32 ",%" PRId32 ",%" PRId32 ")\n", a.x, a.y, a.z, a.a); a.x += 1; a.y -= 2; a.z += 3; @@ -332,7 +332,7 @@ struct5 test_5(struct5 a) struct6 test_6(struct6 a) { - if (verbose) printf("(%" PRId64 ",%" PRId64 ",%" PRId64 ")\n", a.x, a.y, a.z); + if (verbose) fprintf(stderr,"(%" PRId64 ",%" PRId64 ",%" PRId64 ")\n", a.x, a.y, a.z); a.x += 1; a.y -= 2; a.z += 3; @@ -341,7 +341,7 @@ struct6 test_6(struct6 a) struct7 test_7(struct7 a) { - if (verbose) printf("(%" PRId64 ",%" PRId8 ")\n", a.x, a.y); + if (verbose) fprintf(stderr,"(%" PRId64 ",%" PRId8 ")\n", a.x, a.y); a.x += 1; a.y -= 2; return a; @@ -349,7 +349,7 @@ struct7 test_7(struct7 a) struct8 test_8(struct8 a) { - if (verbose) printf("(%" PRId32 ",%" PRId8 ")\n", a.x, a.y); + if (verbose) fprintf(stderr,"(%" PRId32 ",%" PRId8 ")\n", a.x, a.y); a.x += 1; a.y -= 2; return a; @@ -357,7 +357,7 @@ struct8 test_8(struct8 a) struct9 test_9(struct9 a) { - if (verbose) printf("(%" PRId32 ",%" PRId16 ")\n", a.x, a.y); + if (verbose) fprintf(stderr,"(%" PRId32 ",%" PRId16 ")\n", a.x, a.y); a.x += 1; a.y -= 2; return a; @@ -365,7 +365,7 @@ struct9 test_9(struct9 a) struct10 test_10(struct10 a) { - if (verbose) printf("(%" PRId8 ",%" PRId8 ",%" PRId8 ",%" PRId8 ")\n", a.x, a.y, a.z, a.a); + if (verbose) fprintf(stderr,"(%" PRId8 ",%" PRId8 ",%" PRId8 ",%" PRId8 ")\n", a.x, a.y, a.z, a.a); a.x += 1; a.y -= 2; a.z += 3; @@ -376,7 +376,7 @@ struct10 test_10(struct10 a) struct14 test_14(struct14 a) { //The C equivalent of a ComplexPair{Float32} struct (but without special complex ABI) - if (verbose) printf("%g + %g i\n", a.x, a.y); + if (verbose) fprintf(stderr,"%g + %g i\n", a.x, a.y); a.x += 1; a.y -= 2; return a; @@ -384,7 +384,7 @@ struct14 test_14(struct14 a) { struct15 test_15(struct15 a) { //The C equivalent of a ComplexPair{Float32} struct (but without special complex ABI) - if (verbose) printf("%g + %g i\n", a.x, a.y); + if (verbose) fprintf(stderr,"%g + %g i\n", a.x, a.y); a.x += 1; a.y -= 2; return a; @@ -393,7 +393,7 @@ struct15 test_15(struct15 a) { #define int128_t struct3b int128_t test_128(int128_t a) { //Unpack a Int128 - if (verbose) printf("0x%016" PRIx64 "%016" PRIx64 "\n", a.y, a.x); + if (verbose) fprintf(stderr,"0x%016" PRIx64 "%016" PRIx64 "\n", a.y, a.x); a.x += 1; if (a.x == 0) a.y += 1; @@ -402,7 +402,7 @@ int128_t test_128(int128_t a) { struct_big test_big(struct_big a) { //Unpack a "big" struct { int, int, char } - if (verbose) printf("%lld %lld %c\n", (long long)a.x, (long long)a.y, a.z); + if (verbose) fprintf(stderr,"%lld %lld %c\n", (long long)a.x, (long long)a.y, a.z); a.x += 1; a.y -= 2; a.z -= 'A'; @@ -410,14 +410,14 @@ struct_big test_big(struct_big a) { } int main() { - printf("all of the following should be 1 except xs[259] = 0\n"); + fprintf(stderr,"all of the following should be 1 except xs[259] = 0\n"); a = 3; b = 259; fptr = (volatile int (*)(unsigned char x))&testUcharX; - if (((size_t)fptr)&((size_t)1) == 1) fptr = NULL; - printf("compiled with: '%s'\nxs[3] = %d\nxs[259] = %d\ntestUcharX(3) = %d\ntestUcharX(%d) = %d\nfptr(3) = %d\nfptr(259) = %d\n", + if ((((size_t)fptr)&((size_t)1)) == 1) fptr = NULL; + fprintf(stderr,"compiled with: '%s'\nxs[3] = %d\nxs[259] = %d\ntestUcharX(3) = %d\ntestUcharX(%d) = %d\nfptr(3) = %d\nfptr(259) = %d\n", xstr(CC), xs[a], xs[b], testUcharX(a), b, testUcharX((unsigned char)b), fptr(a), fptr(b)); - printf("misc tests:\n"); + fprintf(stderr,"misc tests:\n"); struct1 a = {352.39422e23, 19.287577}; a = test_1(a); } From dd34fa50753f7696514e19981421899376e5a1a0 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 4 Feb 2015 02:44:09 -0500 Subject: [PATCH 07/14] implement Ref{T} support in cfunction --- src/alloc.c | 2 +- src/ccall.cpp | 2 +- src/codegen.cpp | 566 ++++++++++++++++++++++++++++++------------------ src/dump.c | 2 +- src/julia.h | 2 +- test/ccall.jl | 3 + 6 files changed, 363 insertions(+), 214 deletions(-) diff --git a/src/alloc.c b/src/alloc.c index 7543319b181d4..8a817d3eb826e 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -464,7 +464,7 @@ jl_lambda_info_t *jl_new_lambda_info(jl_value_t *ast, jl_tuple_t *sparams) li->roots = NULL; li->functionObject = NULL; li->specFunctionObject = NULL; - li->cFunctionObject = NULL; + li->cFunctionList = NULL; li->functionID = 0; li->specFunctionID = 0; li->specTypes = NULL; diff --git a/src/ccall.cpp b/src/ccall.cpp index 8908d1cb14571..532ff81697f31 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -239,7 +239,7 @@ static Value *runtime_sym_lookup(PointerType *funcptype, char *f_lib, char *f_na Value *llvm_type_rewrite(Value *v, Type *target_type, jl_value_t *ty, bool isret) { - if(preferred_llvm_type(ty,isret) == NULL || target_type == NULL || target_type == v->getType()) + if (preferred_llvm_type(ty,isret) == NULL || target_type == NULL || target_type == v->getType()) return v; assert(!v->getType()->isPointerTy()); diff --git a/src/codegen.cpp b/src/codegen.cpp index 82ed3b7709920..7e92cc0349192 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -536,6 +536,14 @@ typedef struct { std::vector to_inline; } jl_codectx_t; +typedef struct { + size_t len; + struct { + int64_t isref; + Function *f; + } data[]; +} simple_list; + static Value *emit_expr(jl_value_t *expr, jl_codectx_t *ctx, bool boxed=true, bool valuepos=true, jl_sym_t **valuevar=NULL); static Value *emit_unboxed(jl_value_t *e, jl_codectx_t *ctx); @@ -592,9 +600,9 @@ static void jl_rethrow_with_add(const char *fmt, ...) // --- entry point --- //static int n_emit=0; -static Function *emit_function(jl_lambda_info_t *lam, bool force_specialized, bool cstyle); +static Function *emit_function(jl_lambda_info_t *lam); //static int n_compile=0; -static Function *to_function(jl_lambda_info_t *li, bool force_specialized, bool cstyle) +static Function *to_function(jl_lambda_info_t *li) { JL_SIGATOMIC_BEGIN(); assert(!li->inInference); @@ -604,15 +612,14 @@ static Function *to_function(jl_lambda_info_t *li, bool force_specialized, bool nested_compile = true; Function *f = NULL; JL_TRY { - f = emit_function(li, force_specialized, cstyle); + f = emit_function(li); //jl_printf(JL_STDOUT, "emit %s\n", li->name->name); //n_emit++; } JL_CATCH { li->functionObject = NULL; - li->cFunctionObject = NULL; li->specFunctionObject = NULL; - li->cFunctionObject = NULL; + li->cFunctionList = NULL; nested_compile = last_n_c; if (old != NULL) { builder.SetInsertPoint(old); @@ -701,41 +708,53 @@ extern "C" void jl_generate_fptr(jl_function_t *f) jl_setup_module(m,true); FunctionMover mover(m,shadow_module); li->functionObject = mover.CloneFunction((Function*)li->functionObject); - if (li->cFunctionObject != NULL) - li->cFunctionObject = mover.CloneFunction((Function*)li->cFunctionObject); + if (li->specFunctionObject != NULL) + li->specFunctionObject = mover.CloneFunction((Function*)li->specFunctionObject); + if (li->cFunctionList != NULL) { + size_t i; + simple_list *list = (simple_list*)li->cFunctionList; + for (i = 0; i < list->len; i++) { + list->data[i].f = mover.CloneFunction(list->data[i].f); + } + } } #endif Function *llvmf = (Function*)li->functionObject; - #ifdef USE_MCJIT li->fptr = (jl_fptr_t)(intptr_t)jl_ExecutionEngine->getFunctionAddress(llvmf->getName()); #else li->fptr = (jl_fptr_t)jl_ExecutionEngine->getPointerToFunction(llvmf); #endif assert(li->fptr != NULL); - if (li->cFunctionObject != NULL) { + if (!imaging_mode) + llvmf->deleteBody(); + + if (li->cFunctionList != NULL) { + size_t i; + simple_list *list = (simple_list*)li->cFunctionList; + for (i = 0; i < list->len; i++) { #ifdef USE_MCJIT - (void)jl_ExecutionEngine->getFunctionAddress(((Function*)li->cFunctionObject)->getName()); + (void)jl_ExecutionEngine->getFunctionAddress(list->data[i].f->getName()); #else - (void)jl_ExecutionEngine->getPointerToFunction((Function*)li->cFunctionObject); + (void)jl_ExecutionEngine->getPointerToFunction(list->data[i].f); #endif + if (!imaging_mode) { + list->data[i].f->deleteBody(); + } + } } + if (li->specFunctionObject != NULL) { #ifdef USE_MCJIT (void)jl_ExecutionEngine->getFunctionAddress(((Function*)li->specFunctionObject)->getName()); #else (void)jl_ExecutionEngine->getPointerToFunction((Function*)li->specFunctionObject); #endif + if (!imaging_mode) + ((Function*)li->specFunctionObject)->deleteBody(); } JL_SIGATOMIC_END(); - if (!imaging_mode) { - llvmf->deleteBody(); - if (li->cFunctionObject != NULL) - ((Function*)li->cFunctionObject)->deleteBody(); - if (li->specFunctionObject != NULL) - ((Function*)li->specFunctionObject)->deleteBody(); - } } f->fptr = li->fptr; } @@ -746,18 +765,7 @@ extern "C" void jl_compile(jl_function_t *f) if (li->functionObject == NULL) { // objective: assign li->functionObject li->inCompile = 1; - (void)to_function(li, false, false); - li->inCompile = 0; - } -} - -void jl_cstyle_compile(jl_function_t *f) -{ - jl_lambda_info_t *li = f->linfo; - if (li->cFunctionObject == NULL) { - // objective: assign li->cFunctionObject - li->inCompile = 1; - (void)to_function(li, true, true); + (void)to_function(li); li->inCompile = 0; } } @@ -765,46 +773,68 @@ void jl_cstyle_compile(jl_function_t *f) // Get the LLVM Function* for the C-callable entry point for a certain function // and argument types. If rt is NULL then whatever return type is present is // accepted. +static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_tuple_t *argt, int64_t isref); static Function *jl_cfunction_object(jl_function_t *f, jl_value_t *rt, jl_value_t *argt) { if (rt) { - JL_TYPECHK(jl_function_ptr, type, rt); - } - JL_TYPECHK(jl_function_ptr, tuple, argt); - JL_TYPECHK(jl_function_ptr, type, argt); - if (jl_is_gf(f) && (rt == NULL || jl_is_leaf_type(rt)) && jl_is_leaf_type(argt)) { - jl_function_t *ff = jl_get_specialization(f, (jl_tuple_t*)argt); + JL_TYPECHK(cfunction, type, rt); + } + JL_TYPECHK(cfunction, tuple, argt); + JL_TYPECHK(cfunction, type, argt); + JL_TYPECHK(cfunction, function, (jl_value_t*)f); + size_t i, nargs = jl_tuple_len(argt); + uint64_t isref = 0; // bit vector of which argument types are a subtype of Type{Ref{T}} + jl_value_t *sigt; // type signature with Ref{} annotations removed + JL_GC_PUSH(&sigt); + sigt = (jl_value_t*)jl_alloc_tuple(nargs); + for (i = 0; i < nargs; i++) { + jl_value_t *ati = jl_tupleref(argt, i); + if (jl_is_abstract_ref_type(ati)) { + ati = jl_tparam0(ati); + if (jl_is_typevar(ati)) + jl_error("cfunction: argument type Ref should have an element type, not Ref{T}"); + isref |= (2<env==(jl_value_t*)jl_null && ff->linfo != NULL) { - if (ff->linfo->cFunctionObject == NULL) { - jl_cstyle_compile(ff); + jl_lambda_info_t *li = ff->linfo; + if (!jl_types_equal((jl_value_t*)li->specTypes, sigt)) { + jl_errorf("cfunction: type signature of %s does not match", + li->name->name); } - if (ff->linfo->cFunctionObject != NULL) { - jl_lambda_info_t *li = ff->linfo; - if (!jl_types_equal((jl_value_t*)li->specTypes, argt)) { - jl_errorf("cfunction: type signature of %s does not match", - li->name->name); - } - if (rt != NULL) { - jl_value_t *astrt = jl_ast_rettype(li, li->ast); - if (!jl_types_equal(astrt, rt)) { - if (astrt == (jl_value_t*)jl_bottom_type) { - if (rt != (jl_value_t*)jl_void_type) { - // a function that doesn't return can be passed to C as void - jl_errorf("cfunction: %s does not return", li->name->name); - } - } - else { - jl_errorf("cfunction: return type of %s does not match", - li->name->name); - } + jl_value_t *astrt = jl_ast_rettype(li, li->ast); + if (rt != NULL) { + if (astrt == (jl_value_t*)jl_bottom_type) { + if (rt != (jl_value_t*)jl_void_type) { + // a function that doesn't return can be passed to C as void + jl_errorf("cfunction: %s does not return", li->name->name); } } - return (Function*)ff->linfo->cFunctionObject; + else if (!jl_subtype(astrt, rt, 0)) { + jl_errorf("cfunction: return type of %s does not match", + li->name->name); + } } + JL_GC_POP(); // kill list: sigt + return gen_cfun_wrapper(ff, astrt, (jl_tuple_t*)argt, isref); } } jl_error("function is not yet c-callable"); - return NULL; } // get the address of a C-callable entry point for a function @@ -906,8 +936,8 @@ void *jl_get_llvmf(jl_function_t *f, jl_tuple_t *types, bool getwrapper) } } else { - if (sf->linfo->cFunctionObject == NULL) { - jl_cstyle_compile(sf); + if (sf->linfo->specFunctionObject == NULL) { + jl_compile(sf); } } if (sf->fptr == &jl_trampoline) { @@ -917,7 +947,7 @@ void *jl_get_llvmf(jl_function_t *f, jl_tuple_t *types, bool getwrapper) llvmf = (Function*)sf->linfo->functionObject; } else { - llvmf = to_function(sf->linfo, false, false); + llvmf = to_function(sf->linfo); } return llvmf; } @@ -2447,18 +2477,9 @@ static Value *emit_known_call(jl_value_t *ff, jl_value_t **args, size_t nargs, return NULL; } -static Value *emit_jlcall(Value *theFptr, Value *theF, jl_value_t **args, +static Value *emit_jlcall(Value *theFptr, Value *theF, int argStart, size_t nargs, jl_codectx_t *ctx) { - // emit arguments - int argStart = ctx->argDepth; - for(size_t i=0; i < nargs; i++) { - jl_sym_t *sym = NULL; - Value *anArg = emit_expr(args[i], ctx, true, true, &sym); - // put into argument space - make_gcroot(boxed(anArg, ctx, expr_type(args[i],ctx)), ctx, sym); - } - // call Value *myargs = Constant::getNullValue(jl_ppvalue_llvmt); if (ctx->argTemp != NULL && nargs > 0) { @@ -2471,6 +2492,20 @@ static Value *emit_jlcall(Value *theFptr, Value *theF, jl_value_t **args, return result; } +static Value *emit_jlcall(Value *theFptr, Value *theF, jl_value_t **args, + size_t nargs, jl_codectx_t *ctx) +{ + // emit arguments + int argStart = ctx->argDepth; + for(size_t i=0; i < nargs; i++) { + jl_sym_t *sym = NULL; + Value *anArg = emit_expr(args[i], ctx, true, true, &sym); + // put into argument space + make_gcroot(boxed(anArg, ctx, expr_type(args[i],ctx)), ctx, sym); + } + return emit_jlcall(theFptr, theF, argStart, nargs, ctx); +} + static Value *emit_call_function_object(jl_function_t *f, Value *theF, Value *theFptr, bool specialized, jl_value_t **args, size_t nargs, @@ -3485,12 +3520,248 @@ static void finalize_gc_frame(jl_codectx_t *ctx) } } +static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_tuple_t *argt, int64_t isref) +{ + jl_lambda_info_t *lam = ff->linfo; + simple_list *list = (simple_list*)lam->cFunctionList; + if (list != NULL) { + size_t i; + for (i = 0; i < list->len; i++) { + if (list->data[i].isref == isref) { + return list->data[i].f; + } + } + } + // Generate a c-callable wrapper + Type *crt = ((isref&1) ? jl_pvalue_llvmt : julia_struct_to_llvm(jlrettype)); + if (crt == NULL) + jl_error("cfunction: return type doesn't correspond to a C type"); + size_t i; + size_t nargs = jl_tuple_len(lam->specTypes); + assert(nargs == jl_tuple_len(argt)); + for(i=0; i < nargs; i++) { + jl_value_t *tti = jl_tupleref(lam->specTypes,i); + if (tti == (jl_value_t*)jl_pointer_type) { + jl_error("cfunction: argument type Ptr should have an element type, Ptr{T}"); + } + } + + std::vector fargt(0); + std::vector fargt_sig(0); + std::vector inRegList(0); + std::vector byRefList(0); + attr_type attrs; + Type *prt = NULL; + int sret = 0; + std::string err_msg = generate_func_sig(&crt, &prt, sret, fargt, fargt_sig, inRegList, byRefList, attrs, + ((isref&1) ? (jl_value_t*)jl_any_type : jlrettype), argt); + if (!err_msg.empty()) + jl_error(err_msg.c_str()); + if (fargt.size() != fargt_sig.size()) + jl_error("va_arg syntax not allowed for cfunction argument list"); + + std::stringstream funcName; + funcName << "jlcapi_" << lam->name->name << "_" << globalUnique++; + + // Create the Function stub + Module *m; +#ifdef USE_MCJIT + if (!imaging_mode) { + m = new Module(funcName.str(), jl_LLVMContext); + jl_setup_module(m,true); + } + else { + m = shadow_module; + } +#else + m = jl_Module; +#endif + + Function *cw = Function::Create(FunctionType::get(sret ? T_void : prt, fargt_sig, false), + imaging_mode ? GlobalVariable::InternalLinkage : GlobalVariable::ExternalLinkage, + funcName.str(), m); + cw->setAttributes(attrs); + BasicBlock *b0 = BasicBlock::Create(jl_LLVMContext, "top", cw); + builder.SetInsertPoint(b0); + DebugLoc noDbg; + builder.SetCurrentDebugLocation(noDbg); + + jl_codectx_t ctx; + ctx.linfo = lam; + allocate_gc_frame(0, &ctx); + ctx.argSpaceInits = &b0->back(); + + // Save the Function object reference + int len = (list ? list->len : 0) + 1; + simple_list *list2 = (simple_list*)realloc(list, sizeof(*list)+sizeof(list->data[0])*len); + if (!list2) + jl_throw(jl_memory_exception); + list2->len = len; + list2->data[len-1].isref = isref; + list2->data[len-1].f = cw; + lam->cFunctionList = list2; + + // See whether this function is specsig or jlcall + jl_compile(ff); + bool specsig; + Function *theFptr; + if (lam->specFunctionObject != NULL) { + theFptr = (Function*)lam->specFunctionObject; + specsig = true; + } + else { + theFptr = (Function*)lam->functionObject; + specsig = false; + if (!theFptr) { + jl_errorf("error compiling %s while creating cfunction", lam->name->name); + } + } + + + // Alright, let's do this! + // let's first emit the arguments + std::vector args; + Function::arg_iterator AI = cw->arg_begin(); + Value *sretPtr = NULL; + if (sret) + sretPtr = AI++; //const Argument &fArg = *AI++; + + for (size_t i=0; i < nargs; i++) { + Value *v = AI++; + if (isref & (2<specTypes, i); + Value *val = builder.CreatePointerCast(v, jl_pvalue_llvmt); + val = builder.CreateConstGEP1_32(val, (unsigned)-1); // rewind to type-tag + if (specsig) { + Type *at = theFptr->getFunctionType()->getParamType(i); + if (at != jl_pvalue_llvmt) { + val = emit_unbox(at, val, jargty); + assert(dyn_cast(val) == 0); + } + args.push_back(val); + } + else { + make_gcroot(val, &ctx); + } + } + else { + jl_value_t *jargty = jl_tupleref(lam->specTypes, i); + Value *val = NULL; + if (fargt[i+sret] == fargt_sig[i+sret]) { + val = v; + } + else { + // undo whatever we did to this poor argument + val = llvm_type_rewrite(v, fargt[i+sret], jargty, false); + } + // and perhaps box it if necessary + if (byRefList[i]) { + val = builder.CreateLoad(val,false); + } + Type *t = julia_type_to_llvm(jargty); + if (t != fargt[i+sret]) { + if (t == jl_pvalue_llvmt) { + Value *mem = emit_newsym(jargty, 1, NULL, &ctx); + if (!mem->getType()->isPointerTy()) { + assert(type_is_ghost(t) && mem->getType() == t); + } + else { + builder.CreateStore(val, builder.CreateBitCast( + emit_nthptr_addr(mem, (size_t)1), val->getType()->getPointerTo())); + if (specsig) + make_gcroot(val, &ctx); + } + val = builder.CreateBitCast(mem, jl_pvalue_llvmt); + } + else { + assert(0); + } + } + if (specsig) { + args.push_back(val); + } + else { + if (val->getType() != jl_pvalue_llvmt) + val = boxed(val, &ctx, jargty); + make_gcroot(val, &ctx); + } + } + } + + // Create the call + Value *r; + if (specsig) { + r = builder.CreateCall(prepare_call(theFptr), ArrayRef(args)); + } + else { + r = emit_jlcall(theFptr, literal_pointer_val((jl_value_t*)ff), 0, nargs, &ctx); + } + + // Prepare the return value + if (isref&1) { + // return a jl_value_t* + if (r->getType() != jl_pvalue_llvmt) { + r = boxed(r, &ctx, jlrettype); + } + } + + // gc pop. Usually this is done when we encounter the return statement + // but here we have to do it manually + Instruction *gcpop = (Instruction*)builder.CreateConstGEP1_32(ctx.gcframe, 1); + ctx.gc_frame_pops.push_back(gcpop); + builder.CreateStore(builder.CreateBitCast(builder.CreateLoad(gcpop, false), jl_ppvalue_llvmt), + prepare_global(jlpgcstack_var)); + + finalize_gc_frame(&ctx); + + if (isref&1) { + r = builder.CreateConstGEP1_32(r, 1); // skip type-tag + builder.CreateRet(r); + } + else { + // return an unboxed value + if (type_is_ghost(crt)) { + assert(type_is_ghost(prt)); + builder.CreateRetVoid(); + } + else { + Value *v = julia_to_native(crt, jlrettype, r, jlrettype, 0, false, false, false, 0, &ctx, NULL); + if (!sret) { + builder.CreateRet(llvm_type_rewrite(v, prt, jlrettype, true)); + } + else { + Value *sretVal = llvm_type_rewrite(v, fargt_sig[0], jlrettype, true); + builder.CreateStore(sretVal, sretPtr); + builder.CreateRetVoid(); + } + } + } + +#ifdef JL_DEBUG_BUILD +#ifdef LLVM35 + llvm::raw_fd_ostream out(1,false); +#endif + if ( +#ifdef LLVM35 + verifyFunction(*cw,&out) +#else + verifyFunction(*cw,PrintMessageAction) +#endif + ) { + cw->dump(); + abort(); + } +#endif + + return cw; +} + // generate a julia-callable function that calls f (AKA lam) static Function *gen_jlcall_wrapper(jl_lambda_info_t *lam, jl_expr_t *ast, Function *f) { std::stringstream funcName; const std::string &fname = f->getName().str(); - funcName << "jlapi_"; + funcName << "jlcall_"; if (fname.compare(0, 6, "julia_") == 0) funcName << fname.substr(6); else @@ -3555,7 +3826,7 @@ static Function *gen_jlcall_wrapper(jl_lambda_info_t *lam, jl_expr_t *ast, Funct } // cstyle = compile with c-callable signature, not jlcall -static Function *emit_function(jl_lambda_info_t *lam, bool force_specialized, bool cstyle) +static Function *emit_function(jl_lambda_info_t *lam) { // step 1. unpack AST and allocate codegen context for this function jl_expr_t *ast = (jl_expr_t*)lam->ast; @@ -3684,24 +3955,19 @@ static Function *emit_function(jl_lambda_info_t *lam, bool force_specialized, bo Function *f = NULL; bool specsig = false; - if (force_specialized && !va && !hasCapt) { - specsig = true; - } - else { - if (!va && !hasCapt && lam->specTypes != NULL && lam->inferred) { - // no captured vars and not vararg - // consider specialized signature - for(size_t i=0; i < jl_tuple_len(lam->specTypes); i++) { - if (jltupleisbits(jl_tupleref(lam->specTypes, i))) { - specsig = true; - break; - } - } - if (jl_tuple_len(lam->specTypes) == 0) - specsig = true; - if (jltupleisbits(jlrettype)) + if (!va && !hasCapt && lam->specTypes != NULL && lam->inferred) { + // no captured vars and not vararg + // consider specialized signature + for(size_t i=0; i < jl_tuple_len(lam->specTypes); i++) { + if (jltupleisbits(jl_tupleref(lam->specTypes, i))) { specsig = true; + break; + } } + if (jl_tuple_len(lam->specTypes) == 0) + specsig = true; + if (jltupleisbits(jlrettype)) + specsig = true; } std::stringstream funcName; @@ -3748,126 +4014,6 @@ static Function *emit_function(jl_lambda_info_t *lam, bool force_specialized, bo lam->functionObject = (void*)fwrap; lam->functionID = jl_assign_functionID(fwrap); } - if (cstyle && lam->cFunctionObject == NULL) { - // Generate a c-callable wrapper - Type *crt = julia_struct_to_llvm(jlrettype); - if (crt == NULL) - jl_error("cfunction: return type doesn't correspond to a C type"); - size_t i; - size_t nargt = jl_tuple_len(lam->specTypes); - for(i=0; i < nargt; i++) { - jl_value_t *tti = jl_tupleref(lam->specTypes,i); - if (tti == (jl_value_t*)jl_pointer_type) { - jl_error("cfunction: argument type Ptr should have an element type, Ptr{T}"); - } - } - - std::vector fargt(0); - std::vector fargt_sig(0); - std::vector inRegList(0); - std::vector byRefList(0); - attr_type attrs; - Type *prt = NULL; - int sret = 0; - std::string err_msg = generate_func_sig(&crt, &prt, sret, fargt, fargt_sig, inRegList, byRefList, attrs, jlrettype, lam->specTypes); - if (!err_msg.empty()) - jl_error(err_msg.c_str()); - - funcName.str(""); - funcName << "jlcapi_" << lam->name->name << "_" << globalUnique; - Function *cw = Function::Create(FunctionType::get(sret ? T_void : prt, fargt_sig, false), - imaging_mode ? GlobalVariable::InternalLinkage : GlobalVariable::ExternalLinkage, - funcName.str(), m); - cw->setAttributes(attrs); - - BasicBlock *b0 = BasicBlock::Create(jl_LLVMContext, "top", cw); - - builder.SetInsertPoint(b0); - DebugLoc noDbg; - builder.SetCurrentDebugLocation(noDbg); - - // Alright, let's do this! - // let's first emit the arguments - size_t nargs = jl_tuple_len(lam->specTypes); - std::vector args; - Function::arg_iterator AI = cw->arg_begin(); - Value *sretPtr = NULL; - if (sret) - sretPtr = AI++; //const Argument &fArg = *AI++; - - for (size_t i=0; i < nargs; i++) { - jl_value_t *jty = jl_tupleref(lam->specTypes,i); - Value *v = AI++; - Value *val = NULL; - if (fargt[i+sret] == fargt_sig[i+sret]) { - val = v; - } - else { - // undo whatever we did to this poor argument - val = llvm_type_rewrite(v, fargt[i+sret], jl_tupleref(lam->specTypes,i), false); - } - // and perhaps box it if necessary - Type *t = julia_type_to_llvm(jty); - if (byRefList[i]) { - val = builder.CreateLoad(val,false); - } - if (t != fargt[i+sret]) { - if (t == jl_pvalue_llvmt) { - if (byRefList[i]) - v = builder.CreateLoad(v,false); - Value *mem = emit_newsym(jty, 1, NULL, &ctx); - if (!mem->getType()->isPointerTy()) { - assert(type_is_ghost(t) && mem->getType() == t); - } - else { - builder.CreateStore(val, builder.CreateBitCast( - emit_nthptr_addr(mem, (size_t)1), val->getType()->getPointerTo())); - } - val = builder.CreateBitCast(mem, jl_pvalue_llvmt); - } - else { - assert(0); - } - } - - args.push_back(val); - } - - Value *r = builder.CreateCall(f, ArrayRef(args)); - - if (type_is_ghost(crt)) { - assert(type_is_ghost(prt)); - builder.CreateRetVoid(); - } - else { - Value *v = julia_to_native(crt, jlrettype, r, jlrettype, 0, false, false, false, 0, &ctx, NULL); - if (!sret) { - builder.CreateRet(llvm_type_rewrite(v, prt, jlrettype, true)); - } - else { - Value *sretVal = llvm_type_rewrite(v, fargt_sig[0], jlrettype, true); - builder.CreateStore(sretVal, sretPtr); - builder.CreateRetVoid(); - } - } - -#ifdef JL_DEBUG_BUILD -#ifdef LLVM35 - llvm::raw_fd_ostream out(1,false); -#endif - if ( -#ifdef LLVM35 - verifyFunction(*cw,&out) -#else - verifyFunction(*cw,PrintMessageAction) -#endif - ) { - cw->dump(); - abort(); - } -#endif - lam->cFunctionObject = (void*)cw; - } } else { f = Function::Create(jl_func_sig, imaging_mode ? GlobalVariable::InternalLinkage : GlobalVariable::ExternalLinkage, diff --git a/src/dump.c b/src/dump.c index a5016c5e2c959..a5501ab164f45 100644 --- a/src/dump.c +++ b/src/dump.c @@ -1121,7 +1121,7 @@ static jl_value_t *jl_deserialize_value_(ios_t *s, jl_value_t *vtag, jl_value_t if(li->capt) gc_wb(li, li->capt); li->fptr = &jl_trampoline; li->functionObject = NULL; - li->cFunctionObject = NULL; + li->cFunctionList = NULL; li->specFunctionObject = NULL; li->inInference = 0; li->inCompile = 0; diff --git a/src/julia.h b/src/julia.h index b99b23fd3d9a8..618ba84b121d3 100644 --- a/src/julia.h +++ b/src/julia.h @@ -164,7 +164,7 @@ typedef struct _jl_lambda_info_t { int8_t inCompile : 1; jl_fptr_t fptr; // jlcall entry point void *functionObject; // jlcall llvm Function - void *cFunctionObject; // c callable llvm Function + void *cFunctionList; // c callable llvm Functions // specialized llvm Function (common core for the other two) void *specFunctionObject; diff --git a/test/ccall.jl b/test/ccall.jl index 5907b279fe5d0..736b1241fea54 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -135,6 +135,9 @@ for (t,v) in ((Complex{Int32},:ci32),(Complex{Int64},:ci64), s end b = ccall(cfunction($(symbol("foo"*string(v))),$t,($t,)),$t,($t,),$v) + b = ccall(cfunction($(symbol("foo"*string(v))),Ref{$t},(Ref{$t},)),Ref{$t},(Ref{$t},),$v) + b = ccall(cfunction($(symbol("foo"*string(v))),$t,(Ref{$t},)),$t,(Ref{$t},),$v) + #b = ccall(cfunction($(symbol("foo"*string(v))),Any,(Ref{$t},)),Any,(Ref{$t},),$v) # broken due to #2818 verbose && println("C: ",b) if ($(t).mutable) @test !(b === a) From 851c0df6363658f2e490f7d5b4f3533fb257f6a7 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sun, 8 Feb 2015 01:44:20 -0500 Subject: [PATCH 08/14] copy any isbits type we received in cfunction as a Ref{T}. this is a more precise reverse of what happens in ccall given an argument of type Ref{T} --- src/ccall.cpp | 2 +- src/codegen.cpp | 85 +++++++++++++++++++++++-------------------------- 2 files changed, 40 insertions(+), 47 deletions(-) diff --git a/src/ccall.cpp b/src/ccall.cpp index 532ff81697f31..d2237640a81d8 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -247,7 +247,7 @@ Value *llvm_type_rewrite(Value *v, Type *target_type, jl_value_t *ty, bool isret // LLVM doesn't allow us to cast values directly, so // we need to use this alloca trick Value *mem = builder.CreateAlloca(target_type); - builder.CreateStore(v,builder.CreateBitCast(mem,v->getType()->getPointerTo())); + builder.CreateStore(v,builder.CreatePointerCast(mem,v->getType()->getPointerTo())); return builder.CreateLoad(mem); } diff --git a/src/codegen.cpp b/src/codegen.cpp index 7e92cc0349192..c3abb495914fe 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -542,7 +542,7 @@ typedef struct { int64_t isref; Function *f; } data[]; -} simple_list; +} cFunctionList_t; static Value *emit_expr(jl_value_t *expr, jl_codectx_t *ctx, bool boxed=true, bool valuepos=true, jl_sym_t **valuevar=NULL); @@ -712,7 +712,7 @@ extern "C" void jl_generate_fptr(jl_function_t *f) li->specFunctionObject = mover.CloneFunction((Function*)li->specFunctionObject); if (li->cFunctionList != NULL) { size_t i; - simple_list *list = (simple_list*)li->cFunctionList; + cFunctionList_t *list = (cFunctionList_t*)li->cFunctionList; for (i = 0; i < list->len; i++) { list->data[i].f = mover.CloneFunction(list->data[i].f); } @@ -732,7 +732,7 @@ extern "C" void jl_generate_fptr(jl_function_t *f) if (li->cFunctionList != NULL) { size_t i; - simple_list *list = (simple_list*)li->cFunctionList; + cFunctionList_t *list = (cFunctionList_t*)li->cFunctionList; for (i = 0; i < list->len; i++) { #ifdef USE_MCJIT (void)jl_ExecutionEngine->getFunctionAddress(list->data[i].f->getName()); @@ -3523,7 +3523,7 @@ static void finalize_gc_frame(jl_codectx_t *ctx) static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_tuple_t *argt, int64_t isref) { jl_lambda_info_t *lam = ff->linfo; - simple_list *list = (simple_list*)lam->cFunctionList; + cFunctionList_t *list = (cFunctionList_t*)lam->cFunctionList; if (list != NULL) { size_t i; for (i = 0; i < list->len; i++) { @@ -3593,7 +3593,7 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t // Save the Function object reference int len = (list ? list->len : 0) + 1; - simple_list *list2 = (simple_list*)realloc(list, sizeof(*list)+sizeof(list->data[0])*len); + cFunctionList_t *list2 = (cFunctionList_t*)realloc(list, sizeof(*list)+sizeof(list->data[0])*len); if (!list2) jl_throw(jl_memory_exception); list2->len = len; @@ -3627,65 +3627,58 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t sretPtr = AI++; //const Argument &fArg = *AI++; for (size_t i=0; i < nargs; i++) { - Value *v = AI++; + Value *val = AI++; + jl_value_t *jargty = jl_tupleref(lam->specTypes, i); + + // figure out how to unpack this type if (isref & (2<specTypes, i); - Value *val = builder.CreatePointerCast(v, jl_pvalue_llvmt); - val = builder.CreateConstGEP1_32(val, (unsigned)-1); // rewind to type-tag - if (specsig) { - Type *at = theFptr->getFunctionType()->getParamType(i); - if (at != jl_pvalue_llvmt) { - val = emit_unbox(at, val, jargty); - assert(dyn_cast(val) == 0); - } - args.push_back(val); + if (!jl_isbits(jargty)) { + val = builder.CreatePointerCast(val, jl_pvalue_llvmt); + val = builder.CreateConstGEP1_32(val, (unsigned)-1); // rewind to type-tag } else { - make_gcroot(val, &ctx); + Type *t = julia_type_to_llvm(jargty); + val = builder.CreatePointerCast(val, t->getPointerTo()); + val = builder.CreateLoad(val, false); } } else { - jl_value_t *jargty = jl_tupleref(lam->specTypes, i); - Value *val = NULL; - if (fargt[i+sret] == fargt_sig[i+sret]) { - val = v; - } - else { + if (fargt[i+sret] != fargt_sig[i+sret]) { // undo whatever we did to this poor argument - val = llvm_type_rewrite(v, fargt[i+sret], jargty, false); + val = llvm_type_rewrite(val, fargt[i+sret], jargty, false); } - // and perhaps box it if necessary if (byRefList[i]) { val = builder.CreateLoad(val,false); } - Type *t = julia_type_to_llvm(jargty); - if (t != fargt[i+sret]) { - if (t == jl_pvalue_llvmt) { - Value *mem = emit_newsym(jargty, 1, NULL, &ctx); - if (!mem->getType()->isPointerTy()) { - assert(type_is_ghost(t) && mem->getType() == t); - } - else { - builder.CreateStore(val, builder.CreateBitCast( - emit_nthptr_addr(mem, (size_t)1), val->getType()->getPointerTo())); - if (specsig) - make_gcroot(val, &ctx); - } - val = builder.CreateBitCast(mem, jl_pvalue_llvmt); + } + + // figure out how to repack this type + Type *at = specsig ? theFptr->getFunctionType()->getParamType(i) : jl_pvalue_llvmt; + if (val->getType() != at) { + if (at == jl_pvalue_llvmt) { + Value *mem = emit_newsym(jargty, 1, NULL, &ctx); + if (mem->getType() == jl_pvalue_llvmt) { + builder.CreateStore(val, builder.CreateBitCast( + emit_nthptr_addr(mem, (size_t)1), val->getType()->getPointerTo())); + val = mem; } else { - assert(0); + val = boxed(mem, &ctx, jargty); } - } - if (specsig) { - args.push_back(val); + if (specsig) + make_gcroot(val, &ctx); } else { - if (val->getType() != jl_pvalue_llvmt) - val = boxed(val, &ctx, jargty); - make_gcroot(val, &ctx); + val = emit_unbox(at, val, jargty); + assert(dyn_cast(val) == 0); } } + + // add to argument list + if (specsig) + args.push_back(val); + else + make_gcroot(val, &ctx); } // Create the call From db70626b65e05d5746b544471e373d303f3b3311 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 9 Feb 2015 01:19:27 -0500 Subject: [PATCH 09/14] write calling-c-and-fortran-code manual section for new ccall/cfunction/Ref types, and improve error messages in code --- doc/manual/calling-c-and-fortran-code.rst | 829 +++++++++++++++------- src/codegen.cpp | 56 +- test/ccall.jl | 65 +- 3 files changed, 666 insertions(+), 284 deletions(-) diff --git a/doc/manual/calling-c-and-fortran-code.rst b/doc/manual/calling-c-and-fortran-code.rst index c9acfc34f2eae..f883932870905 100644 --- a/doc/manual/calling-c-and-fortran-code.rst +++ b/doc/manual/calling-c-and-fortran-code.rst @@ -50,13 +50,16 @@ Finally, you can use ``ccall`` to actually generate a call to the library function. Arguments to ``ccall`` are as follows: 1. (:function, "library") pair (must be a constant, but see below). -2. Return type, which may be any bits type, including ``Int32``, - ``Int64``, ``Float64``, or ``Ptr{T}`` for any type parameter ``T``, - indicating a pointer to values of type ``T``, or ``Ptr{Void}`` for - ``void*`` "untyped pointer" values. -3. A tuple of input types, like those allowed for the return type. - The input types must be written as a literal tuple, not a tuple-valued - variable or expression. + +2. Return type (see below for mapping the declared C type to Julia) + + - This argument will be evaluated at compile-time. + +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. + 4. The following arguments, if any, are the actual argument values passed to the function. @@ -77,7 +80,7 @@ is that a 1-tuple must be written with a trailing comma. For example, to call the ``getenv`` function to get a pointer to the value of an environment variable, one makes a call like this:: - julia> path = ccall( (:getenv, "libc"), Ptr{UInt8}, (Ptr{UInt8},), "SHELL") + julia> path = ccall((:getenv, "libc"), Ptr{UInt8}, (Ptr{UInt8},), "SHELL") Ptr{UInt8} @0x00007fff5fbffc45 julia> bytestring(path) @@ -85,7 +88,8 @@ of an environment variable, one makes a call like this:: Note that the argument type tuple must be written as ``(Ptr{UInt8},)``, rather than ``(Ptr{UInt8})``. This is because ``(Ptr{UInt8})`` is just -``Ptr{UInt8}``, rather than a 1-tuple containing ``Ptr{UInt8}``:: +the expression ``Ptr{UInt8}`` surrounded by parentheses, rather than +a 1-tuple containing ``Ptr{UInt8}``:: julia> (Ptr{UInt8}) Ptr{UInt8} @@ -104,7 +108,7 @@ in `env.jl `_:: function getenv(var::AbstractString) - val = ccall( (:getenv, "libc"), + val = ccall((:getenv, "libc"), Ptr{UInt8}, (Ptr{UInt8},), var) if val == C_NULL error("getenv: undefined variable: ", var) @@ -129,10 +133,11 @@ machine's hostname:: function gethostname() hostname = Array(UInt8, 128) - ccall( (:gethostname, "libc"), Int32, - (Ptr{UInt8}, UInt), - hostname, length(hostname)) - return bytestring(convert(Ptr{UInt8}, hostname)) + ccall((:gethostname, "libc"), Int32, + (Ptr{UInt8}, Csize_t), + hostname, sizeof(hostname)) + hostname[end] = 0; # ensure null-termination + return bytestring(pointer(hostname)) end This example first allocates an array of bytes, then calls the C library @@ -144,248 +149,545 @@ memory to be passed to the callee and filled in. Allocation of memory from Julia like this is generally accomplished by creating an uninitialized array and passing a pointer to its data to the C function. -A prefix ``&`` is used to indicate that a pointer to a scalar argument -should be passed instead of the scalar value itself (required for all -Fortran function arguments, as noted above). The following -example computes a dot product using a BLAS function. +Creating C-Compatible Julia Function Pointers +--------------------------------------------- -:: +It is possible to pass Julia functions to native functions that accept function +pointer arguments by creating function pointers via the `cfunction` function. - function compute_dot(DX::Vector{Float64}, DY::Vector{Float64}) - assert(length(DX) == length(DY)) - n = length(DX) - incx = incy = 1 - product = ccall( (:ddot_, "libLAPACK"), - Float64, - (Ptr{Int32}, Ptr{Float64}, Ptr{Int32}, Ptr{Float64}, Ptr{Int32}), - &n, DX, &incx, DY, &incy) - return product +Finally, you can use ``cfunction`` to actually generate a call to the +Julia library function. Arguments to ``cfunction`` are as follows: + +1. A Julia Function + +2. Return type + +3. A tuple of input types + +A classic example is the standard C library ``qsort`` function, +declared as:: + + void qsort(void *base, size_t nmemb, size_t size, + int(*compare)(const void *a, const void *b)); + +The ``base`` argument is a pointer to an array of length ``nmemb``, with elements of +``size`` bytes each. ``compare`` is a callback function which takes pointers to two +elements ``a`` and ``b`` and returns an integer less/greater than zero if ``a`` should +appear before/after ``b`` (or zero if any order is permitted). Now, suppose that we +have a 1d array ``A`` of values in Julia that we want to sort using the ``qsort`` +function (rather than Julia’s built-in ``sort`` function). Before we worry about calling +``qsort`` and passing arguments, we need to write a comparison function that works for +some arbitrary type T:: + + function mycompare{T}(a::T, b::T) + return convert(Cint, a < b ? -1 : a > b ? +1 : 0)::Cint end -The meaning of prefix ``&`` is not quite the same as in C. In -particular, any changes to the referenced variables will not be -visible in Julia unless the type is mutable (declared via -``type``). However, even for immutable types it will not cause any -harm for called functions to attempt such modifications (that is, -writing through the passed pointers). Moreover, ``&`` may be used with -any expression, such as ``&0`` or ``&f(x)``. +Notice that we have to be careful about the return type: ``qsort`` expects a function +returning a C ``int``, so we must be sure to return ``Cint`` via a call to ``convert`` +and a ``typeassert``. + +In order to pass this function to C, we obtain its address using the function ``cfunction``:: + + const mycompare_c = cfunction(mycompare, Cint, (Ref{Cdouble}, Ref{Cdouble})) + +``cfunction`` accepts three arguments: the Julia function (``mycompare``), the return +type (``Cint``), and a tuple of the argument types, in this case to sort an array of +``Cdouble`` (Float64) elements. + +The final call to ``qsort`` looks like this:: + + A = [1.3, -2.7, 4.4, 3.1] + ccall(:qsort, Void, (Ptr{Cdouble}, Csize_t, Csize_t, Ptr{Void}), + A, length(A), sizeof(eltype(A)), mycompare_c) + +After this executes, ``A`` is changed to the sorted array ``[-2.7, 1.3, 3.1, 4.4]``. +Note that Julia knows how to convert an array into a ``Ptr{Cdouble}``, how to compute +the size of a type in bytes (identical to C’s ``sizeof`` operator), and so on. +For fun, try inserting a ``println("mycompare($a,$b)")`` line into ``mycompare``, which +will allow you to see the comparisons that ``qsort`` is performing (and to verify that +it is really calling the Julia function that you passed to it). -Currently, it is not possible to reliably pass structs and other non-primitive -types by *value* from Julia to/from C libraries. However, *pointers* -to structs can be passed. The simplest case is that of C functions -that generate and use *opaque* pointers to struct types, which can be -passed to/from Julia as ``Ptr{Void}`` (or any other ``Ptr`` -type). Memory allocation and deallocation of such objects must be -handled by calls to the appropriate cleanup routines in the libraries -being used, just like in any C program. A more complicated approach -is to declare a composite type in Julia that mirrors a C struct, which -allows the structure fields to be directly accessed in Julia. Given a -Julia variable ``x`` of that type, a pointer can be passed as ``&x`` -to a C function expecting a pointer to the corresponding struct. If -the Julia type ``T`` is ``immutable``, then a Julia ``Array{T}`` is -stored in memory identically to a C array of the corresponding struct, -and can be passed to a C program expecting such an array pointer. - -Note that no C header files are used anywhere in the process: you are -responsible for making sure that your Julia types and call signatures -accurately reflect those in the C header file. (The `Clang package -` can be used to generate Julia -code from a C header file.) Mapping C Types to Julia ------------------------ +It is critical to exactly match the declared C type with its declaration +in Julia. Inconsistencies can cause code that works correctly on one system +to fail or produce indeterminate results on a different system. + +Note that no C header files are used anywhere in the process of calling C +functions: you are responsible for making sure that your Julia types and +call signatures accurately reflect those in the C header file. (The `Clang +package` can be used to auto-generate +Julia code from a C header file.) + +Auto-conversion: +~~~~~~~~~~~~~~~~ + Julia automatically inserts calls to the ``convert`` function to convert each argument to the specified type. For example, the following call:: - ccall( (:foo, "libfoo"), Void, (Int32, Float64), - x, y) + ccall((:foo, "libfoo"), Void, (Int32, Float64), x, y) will behave as if the following were written:: - ccall( (:foo, "libfoo"), Void, (Int32, Float64), - convert(Int32, x), convert(Float64, y)) + ccall((:foo, "libfoo"), Void, (Int32, Float64), + Base.cconvert(Int32, Base.cconvert_gcroot(Int32, x)), + Base.cconvert(Float64, Base.cconvert_gcroot(Float64, y))) -When a scalar value is passed with ``&`` as an argument of type -``Ptr{T}``, the value will first be converted to type ``T``. +Note that the primary fall-back method for ``cconvert`` is:: -Array conversions -~~~~~~~~~~~~~~~~~ + cconvert(T,x) = convert(T, x) + +and the primary fallback method for ``cconvert_gcroot`` is:: + + cconvert_gcroot(T,x) = x + +Type Correspondences: +~~~~~~~~~~~~~~~~~~~~~ + +First, a review of some relevant Julia type terminology: -When an array is passed to C as a ``Ptr{T}`` argument, it is -never converted: Julia simply checks that the element type of the -array matches ``T``, and the address of the first element is passed. -This is done in order to avoid copying arrays unnecessarily. +.. rst-class:: text-wrap -Therefore, if an ``Array`` contains data in the wrong format, it will -have to be explicitly converted using a call such as ``Vector{Int32}(a)``. +============================== ============================== ====================================================== +Syntax / Keyword Example Description +============================== ============================== ====================================================== +``type`` ``ASCIIString`` "Leaf Type" :: A group of related data that includes + a type-tag, is managed by the Julia GC, and + is defined by object-identity -To pass an array ``A`` as a pointer of a different type *without* -converting the data beforehand (for example, to pass a ``Float64`` array -to a function that operates on uninterpreted bytes), you can either -declare the argument as ``Ptr{Void}`` or you can explicitly call -``convert(Ptr{T}, pointer(A))``. +``abstract`` ``Any``, "Super Type" :: A super-type (not a leaf-type) + ``AbstractArray{T,N}``, that cannot be instantiated, but can be used to + ``Complex{T}`` describe a group of types +``{T}`` ``Vector{Int}`` "Type Parameter" :: A specialization of a type + (typically used to dispatch or storage optimization) + +``bitstype`` ``Int``, "Bits Type" :: A type with no fields, but a size. It + ``Float64`` is stored and defined by-value + +``immutable`` ``Pair{String,String}`` "Immutable" :: A type with all fields defined to be + constant. It is defined by-value. And may be stored + with a type-tag. + + ``Complex128`` (`isbits`) "Is-Bits" :: A ``bitstype``, or an ``immutable`` type + where all fields are other ``isbits`` types. It is + defined by-value, and is stored without a type-tag + +``type ...; end`` ``nothing`` "Singleton" :: a Leaf Type or Immutable with no fields + +``(...)`` or ``tuple(...)``` ``(1,2,3)`` "Tuple" :: an immutable data-structure similar to an + anonymous immutable type, or a constant array. it's + storage semantics are TBD. + +``typealias`` Not applicable here Type aliases, and other similar mechanisms of + doing type indirection, are resolved to their base + type (this includes assigning a type to another name, + or getting the type out of a function call) +============================== ============================== ====================================================== + +Bits Types: +~~~~~~~~~~~ + +There are several special types to be aware of, as no other type can be defined to behave the same: + +:Float32: Exactly corresponds to the ``float`` type in C (or ``REAL*4`` in Fortran) +:Float64: Exactly corresponds to the ``double`` type in C (or ``REAL*8`` in Fortran) +:Complex64: Exactly corresponds to the ``complex float`` type in C (or ``COMPLEX*8`` in Fortran) +:Complex128: 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 +:Ref{T}: Behaves like a ``Ptr{T}`` that owns it's memory +:Array{T,N}: + When an array is passed to C as a ``Ptr{T}`` argument, it is + not reinterpret-cast: Julia requires that the element type of the + array matches ``T``, and then address of the first element is passed. + + Therefore, if an ``Array`` contains data in the wrong format, it will + have to be explicitly converted using a call such as ``int32(a)``. + + To pass an array ``A`` as a pointer of a different type *without* + converting the data beforehand (for example, to pass a ``Float64`` array + to a function that operates on uninterpreted bytes), you can either + declare the argument as ``Ptr{Void}`` or you can explicitly call + ``pointer(A)``. + + If an array of eltype ``Ptr{T}`` is passed as a ``Ptr{Ptr{T}}`` argument, the Julia base library + `cconvert_gcroot` function will attempt to first make a null-terminated copy of the array with + each element replaced by it's ``cconvert`` version. This allows, for example, passing an ``argv`` + pointer array of type ``Vector{ByteString}`` to an argument of type ``Ptr{Ptr{Cchar}}``. -Type correspondences -~~~~~~~~~~~~~~~~~~~~ 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). -**System-independent:** - -+------------------------+-------------------+--------------------------------+ -| ``unsigned char`` | ``Cuchar`` | ``UInt8`` | -+------------------------+-------------------+--------------------------------+ -| ``short`` | ``Cshort`` | ``Int16`` | -+------------------------+-------------------+--------------------------------+ -| ``unsigned short`` | ``Cushort`` | ``UInt16`` | -+------------------------+-------------------+--------------------------------+ -| ``int`` | ``Cint`` | ``Int32`` | -+------------------------+-------------------+--------------------------------+ -| ``unsigned int`` | ``Cuint`` | ``UInt32`` | -+------------------------+-------------------+--------------------------------+ -| ``long long`` | ``Clonglong`` | ``Int64`` | -+------------------------+-------------------+--------------------------------+ -| ``unsigned long long`` | ``Culonglong`` | ``UInt64`` | -+------------------------+-------------------+--------------------------------+ -| ``intmax_t`` | ``Cintmax_t`` | ``Int64`` | -+------------------------+-------------------+--------------------------------+ -| ``uintmax_t`` | ``Cuintmax_t`` | ``UInt64`` | -+------------------------+-------------------+--------------------------------+ -| ``float`` | ``Cfloat`` | ``Float32`` | -+------------------------+-------------------+--------------------------------+ -| ``double`` | ``Cdouble`` | ``Float64`` | -+------------------------+-------------------+--------------------------------+ -| ``ptrdiff_t`` | ``Cptrdiff_t`` | ``Int`` | -+------------------------+-------------------+--------------------------------+ -| ``ssize_t`` | ``Cssize_t`` | ``Int`` | -+------------------------+-------------------+--------------------------------+ -| ``size_t`` | ``Csize_t`` | ``UInt`` | -+------------------------+-------------------+--------------------------------+ -| ``void`` | | ``Void`` | -+------------------------+-------------------+--------------------------------+ -| ``void*`` | | ``Ptr{Void}`` | -+------------------------+-------------------+--------------------------------+ -| ``char*`` (or ``char[]``, e.g. a string) | ``Ptr{UInt8}`` | -+------------------------+-------------------+--------------------------------+ -| ``char**`` (or ``*char[]``) | ``Ptr{Ptr{UInt8}}`` | -+------------------------+-------------------+--------------------------------+ -| ``struct T*`` (where T represents an | ``Ptr{T}`` (call using | -| appropriately defined bits type) | &variable_name in the | -| | parameter list) | -+------------------------+-------------------+--------------------------------+ -| ``jl_value_t*`` (any Julia Type) | ``Ptr{Any}`` | -+------------------------+-------------------+--------------------------------+ - -Julia's ``Char`` type is 32 bits, which is not the same as the wide -character type (``wchar_t`` or ``wint_t``) on all platforms. - -A C function declared to return ``void`` will give ``nothing`` in Julia. +**System Independent:** + +.. rst-class:: text-wrap + ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| C name | Fortran name | Standard Julia Alias | Julia Base Type | ++===================================+=================+======================+===================================+ +| ``unsigned char`` | ``CHARACTER`` | ``Cuchar`` | ``UInt8`` | +| | | | | +| ``bool`` (`C++`) | | | | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``short`` | ``INTEGER*2`` | ``Cshort`` | ``Int16`` | +| | | | | +| | ``LOGICAL*2`` | | | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``unsigned short`` | | ``Cushort`` | ``UInt16`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``int`` | ``INTEGER*4`` | ``Cint`` | ``Int32`` | +| | | | | +| ``BOOL`` (`C`, typical) | ``LOGICAL*4`` | | | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``unsigned int`` | | ``Cuint`` | ``UInt32`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``long long`` | ``INTEGER*8`` | ``Clonglong`` | ``Int64`` | +| | | | | +| | ``LOGICAL*8`` | | | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``unsigned long long`` | | ``Culonglong`` | ``UInt64`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``intmax_t`` | | ``Cintmax_t`` | ``Int64`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``uintmax_t`` | | ``Cuintmax_t`` | ``UInt64`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``float`` | ``REAL*4i`` | ``Cfloat`` | ``Float32`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``double`` | ``REAL*8`` | ``Cdouble`` | ``Float64`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``complex float`` | ``COMPLEX*8`` | ``Complex64`` | ``Complex{Float32}`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``complex double`` | ``COMPLEX*16`` | ``Complex128`` | ``Complex{Float64}`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``ptrdiff_t`` | | ``Cptrdiff_t`` | ``Int`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``ssize_t`` | | ``Cssize_t`` | ``Int`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``size_t`` | | ``Csize_t`` | ``UInt`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``void`` | | | ``Void`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``void*`` | | | ``Ptr{Void}`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``T*`` (where T represents an | | | ``Ref{T}`` | +| appropriately defined type) | | | | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``char*`` | ``CHARACTER*N`` | | ``Ptr{UInt8}`` | +| (or ``char[]``, e.g. a string) | | | | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``char**`` (or ``*char[]``) | | | ``Ptr{Ptr{UInt8}}`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``jl_value_t*`` | | | ``Any`` | +| (any Julia Type) | | | | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``jl_value_t**`` | | | ``Ref{Any}`` | +| (a reference to a Julia Type) | | | | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``va_arg`` | | | Not supported | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``...`` | | | ```T...``` (where ``T`` | +| (variadic function specification) | | | is one of the above types, | +| | | | variadic functions of different | +| | | | argument types are not supported) | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ **System-dependent:** -====================== ============== ======= -``char`` ``Cchar`` ``Int8`` (x86, x86_64) +====================== ====================== ======= +C name Standard Julia Alias Julia Base Type +====================== ====================== ======= +``char`` ``Cchar`` ``Int8`` (x86, x86_64) - ``UInt8`` (powerpc, arm) -``long`` ``Clong`` ``Int`` (UNIX) + ``UInt8`` (powerpc, arm) - ``Int32`` (Windows) -``unsigned long`` ``Culong`` ``UInt`` (UNIX) +``long`` ``Clong`` ``Int`` (UNIX) - ``UInt32`` (Windows) -``wchar_t`` ``Cwchar_t`` ``Int32`` (UNIX) + ``Int32`` (Windows) - ``UInt16`` (Windows) -====================== ============== ======= +``unsigned long`` ``Culong`` ``UInt`` (UNIX) -For string arguments (``char*``) the Julia type should be ``Ptr{UInt8}``, -not ``ASCIIString``. C functions that take an argument of the type ``char**`` -can be called by using a ``Ptr{Ptr{UInt8}}`` type within Julia. For example, -C functions of the form:: + ``UInt32`` (Windows) - int main(int argc, char **argv); +``wchar_t`` ``Cwchar_t`` ``Int32`` (UNIX) -can be called via the following Julia code:: + ``UInt16`` (Windows) +====================== ====================== ======= - argv = [ "a.out", "arg1", "arg2" ] - ccall(:main, Int32, (Int32, Ptr{Ptr{UInt8}}), length(argv), argv) +`Remember`: when calling a Fortran function, all inputs must be passed by reference, so all type correspondences +above should contain an additional ``Ptr{..}`` or ``Ref{..}`` wrapper around their type specification. + +`Warning`: For string arguments (``char*``) the Julia type should be ``Ptr{Cchar}``, +not ``ASCIIString``. Similarly, for array arguments (``T[]`` or ``T*``), the Julia +type should again be ``Ptr{T}``, not ``Vector{T}``. -For ``wchar_t*`` arguments, the Julia type should be ``Ptr{Wchar_t}``, +`Warning`: Julia's ``Char`` type is 32 bits, which is not the same as the wide +character type (``wchar_t`` or ``wint_t``) on all platforms. + +`Note`: For ``wchar_t*`` arguments, the Julia type should be ``Ptr{Wchar_t}``, and data can be converted to/from ordinary Julia strings by the ``wstring(s)`` function (equivalent to either ``utf16(s)`` or ``utf32(s)`` depending upon the width of ``Cwchar_t``. Note also that ASCII, UTF-8, UTF-16, and UTF-32 string data in Julia is internally NUL-terminated, so it can be passed to C functions expecting NUL-terminated data without making a copy. +`Note`: C functions that take an argument of the type ``char**`` can be called by using +a ``Ptr{Ptr{UInt8}}`` type within Julia. For example, +C functions of the form:: -Accessing Data through a Pointer --------------------------------- -The following methods are described as "unsafe" because they can cause Julia -to terminate abruptly or corrupt arbitrary process memory due to a bad pointer -or type declaration. + int main(int argc, char **argv); -Given a ``Ptr{T}``, the contents of type ``T`` can generally be copied from -the referenced memory into a Julia object using ``unsafe_load(ptr, [index])``. The -index argument is optional (default is 1), and performs 1-based indexing. This -function is intentionally similar to the behavior of ``getindex()`` and ``setindex!()`` -(e.g. ``[]`` access syntax). +can be called via the following Julia code:: -The return value will be a new object initialized -to contain a copy of the contents of the referenced memory. The referenced -memory can safely be freed or released. + argv = [ "a.out", "arg1", "arg2" ] + ccall(:main, Int32, (Int32, Ptr{Ptr{UInt8}}), length(argv), argv) -If ``T`` is ``Any``, then the memory is assumed to contain a reference to -a Julia object (a ``jl_value_t*``), the result will be a reference to this object, -and the object will not be copied. You must be careful in this case to ensure -that the object was always visible to the garbage collector (pointers do not -count, but the new reference does) to ensure the memory is not prematurely freed. -Note that if the object was not originally allocated by Julia, the new object -will never be finalized by Julia's garbage collector. If the ``Ptr`` itself -is actually a ``jl_value_t*``, it can be converted back to a Julia object -reference by ``unsafe_pointer_to_objref(ptr)``. (Julia values ``v`` -can be converted to ``jl_value_t*`` pointers, as ``Ptr{Void}``, by calling -``pointer_from_objref(v)``.) +`Note`: A C function declared to return ``Void`` will return the value ``nothing`` in Julia. -The reverse operation (writing data to a Ptr{T}), can be performed using -``unsafe_store!(ptr, value, [index])``. Currently, this is only supported -for bitstypes or other pointer-free (``isbits``) immutable types. +Struct Type correspondences +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Any operation that throws an error is probably currently unimplemented -and should be posted as a bug so that it can be resolved. +Composite types, aka ``struct`` in C or ``STRUCTURE`` / ``RECORD`` in Fortran), +can be mirrored in Julia by creating a ``type`` or ``immutable`` +definition with the same field layout. -If the pointer of interest is a plain-data array (bitstype or immutable), the -function ``pointer_to_array(ptr,dims,[own])`` may be more useful. The final -parameter should be true if Julia should "take ownership" of the underlying -buffer and call ``free(ptr)`` when the returned ``Array`` object is finalized. -If the ``own`` parameter is omitted or false, the caller must ensure the -buffer remains in existence until all access is complete. +When used recursively, ``isbits`` types are stored inline. +All other types are stored as a pointer to the data. +When mirroring a struct used by-value inside another struct in C, +it is imperative that you do not attempt to manually copy the fields over, +as this will not preserve the correct field alignment. +Instead, declare an immutable isbits type and use that instead. +Unnamed structs are not possible in the translation to Julia. -Arithmetic on the ``Ptr`` type in Julia (e.g. using ``+``) does not behave the -same as C's pointer arithmetic. Adding an integer to a ``Ptr`` in Julia always -moves the pointer by some number of *bytes*, not elements. This way, the -address values obtained from pointer arithmetic do not depend on the -element types of pointers. +Packed structs and union declarations are not supported by Julia. + +You can get a near approximation of a ``union`` if you know, a priori, +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:: + + in C: + struct B { + int A[3]; + }; + b_a_2 = B.A[2]; + + in Julia: + immutable B_A + A_1::Cint + A_2::Cint + A_3::Cint + end + type B + A::B_A + end + b_a_2 = B.A.(2) + +Arrays of unknown size are not supported. + +In the future, some of these restrictions may be reduced or eliminated. + +Memory Ownership: +~~~~~~~~~~~~~~~~~ + +**malloc/free** + +Memory allocation and deallocation of such objects must be +handled by calls to the appropriate cleanup routines in the libraries +being used, just like in any C program. Do not try to free an object +received from a C library with `c_free` in Julia, as this may result +in the ``free`` function being called via the wrong `libc` library and +cause Julia to crash. The reverse (passing an object allocated in Julia +to be freed by an external library) is equally invalid. + +**Ptr{T} vs. Array{T} vs. Ref{T} vs. T** + +The choice of type-wrapper declaration strongly depends on who allocated the memory, +and the declared type. +In general, use ``T`` if the memory is intended to be allocated in +(and managed by) Julia (with type-tag). +Use ``Ptr{T}`` if the memory is expected to be populated by ``C`` (without type-tag). +Use ``Ref{T}`` if you have an ``isbits`` type, +but you want to turn it into a pointer to a struct in another struct definition. + +See issue #2818 for some work that needs to be done to simplify this so that Julia +types can be used to recursively mirror c-style structs, +without requiring as much manual management of the ``Ptr`` conversions. +After #2818 is implemented, it will be true that an `Vector{T}` will be equivalent to +an `Ptr{Ptr{T}}`. That is currently not true, and the conversion must be explicitly. + +Mapping C Functions to Julia +---------------------------- + +ccall/cfunction argument translation guide +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For translating a ``c`` argument list to ``Julia``: + +* ``T``, where ``T`` is one of the primitive types: + ``char``, ``int``, ``long``, ``short``, ``float``, ``double``, ``complex``, ``enum`` + or any of their `typedef` equivalents + + + ``T``, where ``T`` is an equivalent Julia Bits Type (per the table above) + + if ``T`` is an ``enum``, the argument type should be equivalent to `Cint` or `Cuint` + + argument value will be copied (passed by-value) + +* ``struct T`` (including typedef to a struct) + + + ``T``, where ``T`` is a Julia Leaf Type + + argument value will be copied (passed by-value) + +* ``void*`` + + + depends on how this parameter is used, first translate this to the intended pointer type, + then determine the Julia equivalent using the remaining rules in this list + + this argument may be declared as ``Ptr{Void}``, if it really is just an unknown pointer + +* ``jl_value_t*`` + + + ``Any`` + + argument value must be a valid Julia object + + currently unsupported by cfunction + +* ``jl_value_t**`` + + + ``Ref{Any}`` + + argument value must be a valid Julia object (or ``C_NULL``) + + currently unsupported by cfunction + +* ``T*`` + + + ``Ref{T}``, where ``T`` is the Julia type corresponding to ``T`` + + argument value will be copied if it is an ``isbits`` type + otherwise, the value must be a valid Julia object + +* ``(T*)(...)`` (e.g. a pointer to a function) + + + ``Ptr{Void}`` (you may need to use ``cfunction`` explicitly to create this pointer) + +* ``...`` (e.g. a vararg) + + + ``T...``, where ``T`` is the Julia type + +* ``va_arg`` + + + not supported + +ccall/cfunction return type translation guide +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For translating a ``c`` return type to ``Julia``: + +* ``void`` + + + ``Void`` (this will return the singleton instance ``nothing::Void``) + +* ``T``, where ``T`` is one of the primitive types: + ``char``, ``int``, ``long``, ``short``, ``float``, ``double``, ``complex``, ``enum`` + or any of their `typedef` equivalents + + + ``T``, where ``T`` is an equivalent Julia Bits Type (per the table above) + + if ``T`` is an ``enum``, the argument type should be equivalent to ``Cint`` or ``Cuint`` + + argument value will be copied (returned by-value) + +* ``struct T`` (including typedef to a struct) + + + ``T``, where ``T`` is a Julia Leaf Type + + argument value will be copied (returned by-value) + +* ``void*`` + + + depends on how this parameter is used, first translate this to the intended pointer type, + then determine the Julia equivalent using the remaining rules in this list + + this argument may be declared as ``Ptr{Void}``, if it really is just an unknown pointer + +* ``jl_value_t*`` + + + ``Any`` + + argument value must be a valid Julia object + +* ``jl_value_t**`` + + + ``Ref{Any}`` + + argument value must be a valid Julia object (or ``C_NULL``) + +* ``T*`` + + + If the memory is already owned by Julia, or is an `isbits` type, and is known to be non-null: + + + ``Ref{T}``, where ``T`` is the Julia type corresponding to ``T`` + + a return type of ``Ref{Any}`` is invalid, it should either be ``Any`` + (corresponding to ``jl_value_t*``) or ``Ptr{Any}`` (corresponding to ``Ptr{Any}``) + + currently partially unsupported by cfunction due to #2818 + + C **MUST NOT** modify the memory returned via ``Ref{T}`` if ``T`` is an ``isbits`` type + + + If the memory is owned by C: + + + ``Ptr{T}``, where ``T`` is the Julia type corresponding to ``T`` + +* ``(T*)(...)`` (e.g. a pointer to a function) + + + ``Ptr{Void}`` (you may need to use ``cfunction`` explicitly to create this pointer) Passing Pointers for Modifying Inputs -------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Because C doesn't support multiple return values, often C functions will take -pointers to data that the function will modify. To accomplish this within a -``ccall`` you need to encapsulate the value inside an array of the appropriate -type. When you pass the array as an argument with a ``Ptr`` type, julia will -automatically pass a C pointer to the encapsulated data:: +Because C doesn't support multiple return values, +often C functions will take pointers to data that the function will modify. +To accomplish this within a ``ccall``, +you need to first encapsulate the value inside an ``Ref{T}`` of the appropriate type. +When you pass this ``Ref`` object as an argument, +julia will automatically pass a C pointer to the encapsulated data:: - width = Cint[0] - range = Cfloat[0] - ccall(:foo, Void, (Ptr{Cint}, Ptr{Cfloat}), width, range) + width = Ref{Cint}(0) + range = Ref{Cfloat}(0) + ccall(:foo, Void, (Ref{Cint}, Ref{Cfloat}), width, range) This is used extensively in Julia's LAPACK interface, where an integer ``info`` is passed to LAPACK by reference, and on return, includes the success code. +Special Reference Syntax for ccall (deprecated): +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + The ``&`` syntax is deprecated, use the ``Ref{T}`` argument type instead + +A prefix ``&`` is used on an argument to ccall to indicate that a pointer +to a scalar argument should be passed instead of the scalar value itself +(required for all Fortran function arguments, as noted above). The following +example computes a dot product using a BLAS function. + +:: + + function compute_dot(DX::Vector{Float64}, DY::Vector{Float64}) + assert(length(DX) == length(DY)) + n = length(DX) + incx = incy = 1 + product = ccall((:ddot_, "libLAPACK"), + Float64, + (Ptr{Int32}, Ptr{Float64}, Ptr{Int32}, Ptr{Float64}, Ptr{Int32}), + &n, DX, &incx, DY, &incy) + return product + end + +The meaning of prefix ``&`` is not quite the same as in C. In +particular, any changes to the referenced variables will not be +visible in Julia unless the type is mutable (declared via +``type``). However, even for immutable types it will not cause any +harm for called functions to attempt such modifications (that is, +writing through the passed pointers). Moreover, ``&`` may be used with +any expression, such as ``&0`` or ``&f(x)``. + +When a scalar value is passed with ``&`` as an argument of type +``Ptr{T}``, the value will first be converted to type ``T``. + + Garbage Collection Safety ------------------------- When passing data to a ccall, it is best to avoid using the ``pointer()`` @@ -395,7 +697,7 @@ preserved from garbage collection until the call returns. If a C API will store a reference to memory allocated by Julia, after the ccall returns, you must arrange that the object remains visible to the garbage collector. The suggested way to handle this is to make a global variable of type -``Array{Any,1}`` to hold these values, until C interface notifies you that +``Array{Ref,1}`` to hold these values, until the C library notifies you that it is finished with them. Whenever you have created a pointer to Julia data, you must ensure the original data @@ -429,14 +731,39 @@ variables will not be available (unless their values are substituted with definitions, for example when wrapping libraries that contain many similar functions. +If your usage is more dynamic, use indirect calls as described in the next section. + + Indirect Calls -------------- -The first argument to ``ccall`` can also be an expression evaluated at -run time. In this case, the expression must evaluate to a ``Ptr``, -which will be used as the address of the native function to call. This -behavior occurs when the first ``ccall`` argument contains references -to non-constants, such as local variables or function arguments. +The first argument to ``ccall`` can also be an expression evaluated at run time. +In this case, the expression must evaluate to a ``Ptr``, +which will be used as the address of the native function to call. +This behavior occurs when the first ``ccall`` argument contains references to non-constants, +such as local variables, function arguments, or non-constant globals. + +For example, you might lookup the function via ``dlsym``, +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)) + z = esc(z) + quote + let $zlocal::Ptr{Void} = $z::Ptr{Void} + if $zlocal == C_NULL + $zlocal = dlsym($(esc(lib))::Ptr{Void}, $(esc(func))) + global $z = $zlocal + end + $zlocal + end + end + end + + mylibvar = dlopen("mylib") + ccall(@dlsym("myfunc", mylibvar), Void, ()) + Calling Convention ------------------ @@ -445,15 +772,17 @@ The second argument to ``ccall`` can optionally be a calling convention specifier (immediately preceding return type). Without any specifier, the platform-default C calling convention is used. Other supported conventions are: ``stdcall``, ``cdecl``, ``fastcall``, and ``thiscall``. -For example (from base/libc.jl):: +For example (from base/libc.jl) we see the same ``gethostname`` ccall as above, +but with the correct signature for Windows:: hn = Array(UInt8, 256) - err=ccall(:gethostname, stdcall, Int32, (Ptr{UInt8}, UInt32), hn, length(hn)) + err = ccall(:gethostname, stdcall, Int32, (Ptr{UInt8}, UInt32), hn, length(hn)) For more information, please see the `LLVM Language Reference`_. .. _LLVM Language Reference: http://llvm.org/docs/LangRef.html#calling-conventions + Accessing Global Variables -------------------------- @@ -468,58 +797,59 @@ the variable:: The result is a pointer giving the address of the value. The value can be manipulated through this pointer using ``unsafe_load`` and ``unsafe_store``. -Passing Julia Callback Functions to C -------------------------------------- - -It is possible to pass Julia functions to native functions that accept function -pointer arguments. A classic example is the standard C library ``qsort`` function, -declared as:: - - void qsort(void *base, size_t nmemb, size_t size, - int(*compare)(const void *a, const void *b)); -The ``base`` argument is a pointer to an array of length ``nmemb``, with elements of -``size`` bytes each. ``compare`` is a callback function which takes pointers to two -elements ``a`` and ``b`` and returns an integer less/greater than zero if ``a`` should -appear before/after ``b`` (or zero if any order is permitted). Now, suppose that we -have a 1d array ``A`` of values in Julia that we want to sort using the ``qsort`` -function (rather than Julia’s built-in sort function). Before we worry about calling -``qsort`` and passing arguments, we need to write a comparison function that works for -some arbitrary type T:: +Accessing Data through a Pointer +-------------------------------- +The following methods are described as "unsafe" because a bad pointer +or type declaration can cause Julia to terminate abruptly +(although, that's quite alike with ccall). - function mycompare{T}(a_::Ptr{T}, b_::Ptr{T}) - a = unsafe_load(a_) - b = unsafe_load(b_) - return convert(Cint, a < b ? -1 : a > b ? +1 : 0) - end +Given a ``Ptr{T}``, the contents of type ``T`` can generally be copied from +the referenced memory into a Julia object using ``unsafe_load(ptr, [index])``. +The index argument is optional (default is 1), +and follows the Julia-convention of 1-based indexing. +This function is intentionally similar to the behavior of ``getindex()`` and ``setindex!()`` +(e.g. ``[]`` access syntax). -Notice that we have to be careful about the return type: ``qsort`` expects a function -returning a C ``int``, so we must be sure to return ``Cint`` via a call to ``convert``. +The return value will be a new object initialized +to contain a copy of the contents of the referenced memory. +The referenced memory can safely be freed or released. -In order to pass this function to C, we obtain its address using the function -``cfunction``:: +If ``T`` is ``Any``, then the memory is assumed to contain a reference to +a Julia object (a ``jl_value_t*``), the result will be a reference to this object, +and the object will not be copied. You must be careful in this case to ensure +that the object was always visible to the garbage collector (pointers do not +count, but the new reference does) to ensure the memory is not prematurely freed. +Note that if the object was not originally allocated by Julia, the new object +will never be finalized by Julia's garbage collector. If the ``Ptr`` itself +is actually a ``jl_value_t*``, it can be converted back to a Julia object +reference by ``unsafe_pointer_to_objref(ptr)``. (Julia values ``v`` +can be converted to ``jl_value_t*`` pointers, as ``Ptr{Void}``, by calling +``pointer_from_objref(v)``.) - const mycompare_c = cfunction(mycompare, Cint, (Ptr{Cdouble}, Ptr{Cdouble})) +The reverse operation (writing data to a Ptr{T}), can be performed using +``unsafe_store!(ptr, value, [index])``. Currently, this is only supported +for bitstypes or other pointer-free (``isbits``) immutable types. -``cfunction`` accepts three arguments: the Julia function (``mycompare``), the return -type (``Cint``), and a tuple of the argument types, in this case to sort an array of -``Cdouble`` (Float64) elements. +Any operation that throws an error is probably currently unimplemented +and should be posted as a bug so that it can be resolved. -The final call to ``qsort`` looks like this:: +If the pointer of interest is a plain-data array (bitstype or immutable), the +function ``pointer_to_array(ptr,dims,[own])`` may be more useful. The final +parameter should be true if Julia should "take ownership" of the underlying +buffer and call ``free(ptr)`` when the returned ``Array`` object is finalized. +If the ``own`` parameter is omitted or false, the caller must ensure the +buffer remains in existence until all access is complete. - A = [1.3, -2.7, 4.4, 3.1] - ccall(:qsort, Void, (Ptr{Cdouble}, Csize_t, Csize_t, Ptr{Void}), - A, length(A), sizeof(eltype(A)), mycompare_c) +Arithmetic on the ``Ptr`` type in Julia (e.g. using ``+``) does not behave the +same as C's pointer arithmetic. Adding an integer to a ``Ptr`` in Julia always +moves the pointer by some number of *bytes*, not elements. This way, the +address values obtained from pointer arithmetic do not depend on the +element types of pointers. -After this executes, ``A`` is changed to the sorted array ``[ -2.7, 1.3, 3.1, 4.4]``. -Note that Julia knows how to convert an array into a ``Ptr{Cdouble}``, how to compute -the size of a type in bytes (identical to C’s ``sizeof`` operator), and so on. -For fun, try inserting a ``println("mycompare($a,$b)")`` line into ``mycompare``, which -will allow you to see the comparisons that ``qsort`` is performing (and to verify that -it is really calling the Julia function that you passed to it). Thread-safety -~~~~~~~~~~~~~ +------------- Some C libraries execute their callbacks from a different thread, and since Julia isn't thread-safe you'll need to take some extra @@ -535,7 +865,7 @@ The callback you pass to C should only execute a ``ccall`` to ``:uv_async_send``, passing ``cb.handle`` as the argument. More About Callbacks -~~~~~~~~~~~~~~~~~~~~ +-------------------- For more details on how to pass callbacks to C libraries, see this `blog post `_. @@ -543,11 +873,11 @@ For more details on how to pass callbacks to C libraries, see this C++ --- -Limited support for C++ is provided by the `Cpp `_ -and `Clang `_ packages. +Limited support for C++ is provided by the `Cpp `_, +`Clang `_, and `Cxx `_ packages. -Handling Platform Variations ----------------------------- +Handling Operating System Variation +----------------------------------- When dealing with platform libraries, it is often necessary to provide special cases for various platforms. The variable ``OS_NAME`` can be used to write these special @@ -574,3 +904,4 @@ Complex blocks:: Chaining (parentheses optional, but recommended for readability):: @windows? :a : (@osx? :b : :c) + diff --git a/src/codegen.cpp b/src/codegen.cpp index c3abb495914fe..489772951db3f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -782,7 +782,13 @@ static Function *jl_cfunction_object(jl_function_t *f, jl_value_t *rt, jl_value_ JL_TYPECHK(cfunction, tuple, argt); JL_TYPECHK(cfunction, type, argt); JL_TYPECHK(cfunction, function, (jl_value_t*)f); + if (!jl_is_gf(f)) + jl_error("only generic functions are currently c-callable"); + size_t i, nargs = jl_tuple_len(argt); + if (nargs >= 64) + jl_error("only functions with less than 64 arguments are c-callable"); + uint64_t isref = 0; // bit vector of which argument types are a subtype of Type{Ref{T}} jl_value_t *sigt; // type signature with Ref{} annotations removed JL_GC_PUSH(&sigt); @@ -795,13 +801,19 @@ static Function *jl_cfunction_object(jl_function_t *f, jl_value_t *rt, jl_value_ jl_error("cfunction: argument type Ref should have an element type, not Ref{T}"); isref |= (2<env==(jl_value_t*)jl_null && ff->linfo != NULL) { - jl_lambda_info_t *li = ff->linfo; - if (!jl_types_equal((jl_value_t*)li->specTypes, sigt)) { - jl_errorf("cfunction: type signature of %s does not match", - li->name->name); - } - jl_value_t *astrt = jl_ast_rettype(li, li->ast); - if (rt != NULL) { - if (astrt == (jl_value_t*)jl_bottom_type) { - if (rt != (jl_value_t*)jl_void_type) { - // a function that doesn't return can be passed to C as void - jl_errorf("cfunction: %s does not return", li->name->name); - } - } - else if (!jl_subtype(astrt, rt, 0)) { - jl_errorf("cfunction: return type of %s does not match", - li->name->name); + jl_function_t *ff = jl_get_specialization(f, (jl_tuple_t*)sigt); + if (ff != NULL && ff->env==(jl_value_t*)jl_null && ff->linfo != NULL) { + jl_lambda_info_t *li = ff->linfo; + if (!jl_types_equal((jl_value_t*)li->specTypes, sigt)) { + jl_errorf("cfunction: type signature of %s does not match specification", + li->name->name); + } + jl_value_t *astrt = jl_ast_rettype(li, li->ast); + if (rt != NULL) { + if (astrt == (jl_value_t*)jl_bottom_type) { + if (rt != (jl_value_t*)jl_void_type) { + // a function that doesn't return can be passed to C as void + jl_errorf("cfunction: %s does not return", li->name->name); } } - JL_GC_POP(); // kill list: sigt - return gen_cfun_wrapper(ff, astrt, (jl_tuple_t*)argt, isref); + else if (!jl_subtype(astrt, rt, 0)) { + jl_errorf("cfunction: return type of %s does not match", + li->name->name); + } } + JL_GC_POP(); // kill list: sigt + return gen_cfun_wrapper(ff, astrt, (jl_tuple_t*)argt, isref); } - jl_error("function is not yet c-callable"); + jl_error("cfunction: no method exactly matched the required type signature (function not yet c-callable)"); } // get the address of a C-callable entry point for a function @@ -3708,7 +3718,7 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t finalize_gc_frame(&ctx); if (isref&1) { - r = builder.CreateConstGEP1_32(r, 1); // skip type-tag + //r = builder.CreateConstGEP1_32(r, 1); // skip type-tag -- broken if you do, broken if you don't builder.CreateRet(r); } else { diff --git a/test/ccall.jl b/test/ccall.jl index 736b1241fea54..097b00afdb900 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -118,30 +118,71 @@ verbose && println("Testing cfunction roundtrip: ") # cfunction roundtrip for (t,v) in ((Complex{Int32},:ci32),(Complex{Int64},:ci64), (Complex64,:cf32),(Complex128,:cf64),(Struct1,:s1)) + fname = symbol("foo"*string(v)) + fname1 = symbol("foo1"*string(v)) @eval begin verbose && println($t) - if($(t).mutable) - a = copy($v) - else - a = $v - end + a = copy($v) verbose && println("A: ",a) - function $(symbol("foo"*string(v)))(s::$t) + function $fname1(s::$t) + verbose && println("B: ",s) + @test s == $v + @test s === a + global c = s + s + end + function $fname1(s) + verbose && println("B(Any): ",s) + @test s == $v + @test s === a + global c = s + s + end + function $fname(s::$t) verbose && println("B: ",s) + @test s == $v + if($(t).mutable) + @test !(s === a) + end + global c = s + s + end + function $fname(s) + verbose && println("B(Any): ",s) + @test s == $v if($(t).mutable) @test !(s === a) end - @test s == a + global c = s s end - b = ccall(cfunction($(symbol("foo"*string(v))),$t,($t,)),$t,($t,),$v) - b = ccall(cfunction($(symbol("foo"*string(v))),Ref{$t},(Ref{$t},)),Ref{$t},(Ref{$t},),$v) - b = ccall(cfunction($(symbol("foo"*string(v))),$t,(Ref{$t},)),$t,(Ref{$t},),$v) - #b = ccall(cfunction($(symbol("foo"*string(v))),Any,(Ref{$t},)),Any,(Ref{$t},),$v) # broken due to #2818 + b = ccall(cfunction($fname1,Ref{$t},(Ref{$t},)),Ref{$t},(Ref{$t},),a) + verbose && println("C: ",b) + @test b == $v + @test b === a + @test b === c + b = ccall(cfunction($fname,$t,($t,)),$t,($t,),a) + verbose && println("C: ",b) + @test b == $v + if ($(t).mutable) + @test !(b === c) + @test !(b === a) + end + b = ccall(cfunction($fname1,$t,(Ref{$t},)),$t,(Ref{$t},),a) + verbose && println("C: ",b) + @test b == $v + if ($(t).mutable) + @test !(b === c) + @test !(b === a) + end + b = ccall(cfunction($fname,Ref{$t},($t,)),Ref{$t},($t,),a) verbose && println("C: ",b) + @test b == $v + @test b === c if ($(t).mutable) @test !(b === a) end - @test a == b + #b = ccall(cfunction($fname,Any,(Ref{Any},)),Any,(Ref{Any},),$v) # unimplemented + #b = ccall(cfunction($fname,Any,(Ref{$t},)),Any,(Ref{$t},),$v) # broken due to #2818 end end From b7bdfc02d06419d2222b56586d83092f7ee07a68 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 9 Feb 2015 20:58:36 -0500 Subject: [PATCH 10/14] more docs, related to the ccall enhancements, for the Ref and Ptr Types --- doc/manual/calling-c-and-fortran-code.rst | 8 +++- doc/stdlib/c.rst | 49 ++++++++++++++++------- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/doc/manual/calling-c-and-fortran-code.rst b/doc/manual/calling-c-and-fortran-code.rst index f883932870905..94ee2e897c6c1 100644 --- a/doc/manual/calling-c-and-fortran-code.rst +++ b/doc/manual/calling-c-and-fortran-code.rst @@ -256,7 +256,10 @@ Syntax / Keyword Example Description ============================== ============================== ====================================================== ``type`` ``ASCIIString`` "Leaf Type" :: A group of related data that includes a type-tag, is managed by the Julia GC, and - is defined by object-identity + is defined by object-identity. + The type parameters of a leaf type must be fully defined + (no `TypeVars` are allowed) + in order for the instance to be constructed. ``abstract`` ``Any``, "Super Type" :: A super-type (not a leaf-type) ``AbstractArray{T,N}``, that cannot be instantiated, but can be used to @@ -265,6 +268,9 @@ Syntax / Keyword Example Description ``{T}`` ``Vector{Int}`` "Type Parameter" :: A specialization of a type (typically used to dispatch or storage optimization) + "TypeVar" :: The ``T`` in the type parameter declaration + is referred to as a TypeVar (short for type variable) + ``bitstype`` ``Int``, "Bits Type" :: A type with no fields, but a size. It ``Float64`` is stored and defined by-value diff --git a/doc/stdlib/c.rst b/doc/stdlib/c.rst index 86c8b924ec0e9..86b15018d86c8 100644 --- a/doc/stdlib/c.rst +++ b/doc/stdlib/c.rst @@ -4,17 +4,17 @@ C Interface ************* -.. function:: ccall((symbol, library) or fptr, RetType, (ArgType1, ...), ArgVar1, ...) +.. function:: ccall((symbol, library) or function_pointer, ReturnType, (ArgumentType1, ...), ArgumentValue1, ...) Call function in C-exported shared library, specified by ``(function name, library)`` tuple, where each component is an ``AbstractString`` or ``Symbol``. Alternatively, ccall may be used to call a function pointer returned by dlsym, but note that this usage is generally discouraged to facilitate future static compilation. Note that the argument type tuple must be a literal tuple, and not a tuple-valued variable or expression. -.. function:: cglobal((symbol, library) or ptr [, Type=Void]) +.. function:: cglobal((symbol, library) [, type=Void]) Obtain a pointer to a global variable in a C-exported shared library, specified exactly as in ``ccall``. Returns a ``Ptr{Type}``, defaulting to ``Ptr{Void}`` if no Type argument is supplied. The values can be read or written by ``unsafe_load`` or ``unsafe_store!``, respectively. -.. function:: cfunction(fun::Function, RetType::Type, (ArgTypes...)) +.. function:: cfunction(function::Function, ReturnType::Type, (ArgumentTypes...)) Generate C-callable function pointer from Julia function. Type annotation of the return value in the callback function is a must for situations where Julia cannot infer the return type automatically. @@ -30,7 +30,7 @@ bar = cfunction(foo, Float64, ()) -.. function:: dlopen(libfile::AbstractString [, flags::Integer]) +.. function:: dlopen(library_file::AbstractString [, flags::Integer]) Load a shared library, returning an opaque handle. @@ -46,7 +46,7 @@ symbols to be available for usage in other shared libraries, in situations where there are dependencies between shared libraries. -.. function:: dlopen_e(libfile::AbstractString [, flags::Integer]) +.. function:: dlopen_e(library_file::AbstractString [, flags::Integer]) Similar to :func:`dlopen`, except returns a ``NULL`` pointer instead of raising errors. @@ -151,24 +151,20 @@ Copy ``N`` elements from collection ``src`` starting at offset ``so``, to array ``dest`` starting at offset ``do``. Returns ``dest``. -.. function:: pointer(a[, index]) +.. function:: pointer(array [, index]) Get the native address of an array or string element. Be careful to ensure that a julia reference to ``a`` exists as long as this pointer will be used. -.. function:: pointer(type, int) - - Convert an integer to a pointer of the specified element type. - -.. function:: pointer_to_array(p, dims[, own]) +.. function:: pointer_to_array(pointer, dims[, take_ownership::Bool]) Wrap a native pointer as a Julia Array object. The pointer element type determines the array element type. ``own`` optionally specifies whether Julia should take ownership of the memory, calling ``free`` on the pointer when the array is no longer referenced. -.. function:: pointer_from_objref(obj) +.. function:: pointer_from_objref(object_instance) Get the memory address of a Julia object as a ``Ptr``. The existence of the resulting ``Ptr`` will not protect the object from garbage collection, so you must ensure @@ -190,7 +186,7 @@ # interrupt-unsafe code ... end - +5 .. function:: reenable_sigint(f::Function) Re-enable Ctrl-C handler during execution of a function. Temporarily @@ -209,10 +205,35 @@ Raises a ``SystemError`` for ``errno`` with the descriptive string ``sysfunc`` if ``bool`` is true -.. function:: strerror(n) +.. function:: strerror(errno) Convert a system call error code to a descriptive string +.. data:: Ptr{T} + + A simple pointer to an arbitrary memory location. + The type objects expected at the memory location is represented by the type parameter. + However, no guarantee is made or implied that the memory is actually valid, + or that it actually represents the data of the specified type. + + ``C_NULL`` represents a generic, invalid or "NULL" pointer. + +.. data:: Ref{T} + + Effectively, this represents and creates a managed pointer. + The type of objects it can contain is specified by the type parameter. + This type is guaranteed to point to valid, Julia-allocated memory + of the correct type (per the type parameter). + + When passed to a `ccall` argument (either as a `Ptr` or `Ref` type), + the `Ref` object will be implicitly converted to a pointer to the + data region of that type. + + The ``Ref`` type is useful for creating garbage-collector safe pointers and + returning values from a function (esp. a c-function), for example. + + There is no generic invalid or "NULL" Ref object. + .. data:: Cchar Equivalent to the native ``char`` c-type From e3d9f85400f88c2762cc4d5a1455c796b9f998c4 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 7 Mar 2015 23:32:04 -0500 Subject: [PATCH 11/14] doc: folders don't make good Makefile targets, since they can (and do) get sometimes touched unexpected, resulting in unncessary rebuilding --- doc/Makefile | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/Makefile b/doc/Makefile index 30e10819afa74..dc803abc94b90 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,5 +1,6 @@ # Makefile for Sphinx documentation -# + +default: helpdb.jl html # You can set these variables from the command line. SPHINXOPTS = @@ -17,10 +18,8 @@ JULIA_ENV = $(JULIAHOME)/deps/julia-env ACTIVATE = $(JULIA_ENV)/bin/activate SPHINX_BUILD = $(JULIA_ENV)/bin/sphinx-build -$(JULIA_ENV): +$(ACTIVATE): $(MAKE) -C $(JULIAHOME)/deps install-virtualenv - -$(ACTIVATE): $(JULIA_ENV) touch -c $@ $(SPHINX_BUILD): $(ACTIVATE) requirements.txt From 0e9704e39e4fe8fd36f095a8e44b9ac3bbc11476 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 7 Mar 2015 23:50:26 -0500 Subject: [PATCH 12/14] doc: update documentation of unsafe functions, add docs for cconvert/unsafe_convert, add NEWS for cfunction breaking changes --- NEWS.md | 38 ++++++ doc/helpdb.jl | 329 ++++++++++++++++++++--------------------------- doc/stdlib/c.rst | 65 +++++++++- 3 files changed, 240 insertions(+), 192 deletions(-) diff --git a/NEWS.md b/NEWS.md index 2c87804f5251a..438c4772531d6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -29,6 +29,12 @@ New language features Language changes ---------------- + * cfunction breaking syntax changes, ccall improved generalized argument conversion, + and Ref type addition. See the section below on [Improvements to ccall and cfunction, and Ref] + + * `convert(Ptr, x::Any)` is now generally deprecated, replaced by `unsafe_convert`. + (You can still explicitly `convert` one pointer type into another if needed.) + * `[x,y]` constructs a vector of `x` and `y` instead of concatenating them ([#3737], [#2488], [#8599]). @@ -247,6 +253,36 @@ Deprecated or removed * `flipud(A)` and `fliplr(A)` have been deprecated in favor of `flipdim(A, 1)` and `flipdim(A, 2)`, respectively ([#10446]). +Improvements to ccall and cfunction, and Ref +-------------------------------------------- + + * a `Ref` abstract type was added, as a general replacement for the `&` special-syntax. + when converting arguments to `Ref`, the result will be a pointer to a Julia gc-managed + memory location of the appropriate type. + Create a `Ref` object wrapper around a value to create a gc-stable reference to it. + + * cfunction syntax has been altered to be exactly the reverse of ccall + - all types are passed by-value (no implied `*` on the argument), and will be + copied to a new Julia object to match the calling convention of the Julia function + - wrapping a type in `Ref{T}` will look up the method applicable to `T`, + but pass it by-reference to a `jl_value_t*`-typed argument. however, + note that it should only be used on memory allocated in Julia and known isbits types + - isbits types are unaffected by this change, however other types need to be wrapped + in ``Ref{...}`` to retain the old behavior + + * ccall now respects the platform ABI. + For portability, it is essential that your ccall/cfunction argument and return + types are declared correctly. + For example, if the type is one of the native C numeric types (such as `int`), + you must pass it exactly as a 32-bit integer type (for example: `bitstype 32 Int32`). + A Float32 or a `[immutable] type` struct with a size of 32-bits is not equivalent. + + * Lowering of ccall arguments now calls ``unsafe_convert(T, cconvert(T, x))``, + instead of just ``cconvert(T,x)``. Accordingly, "unsafe" operations, like Array-to-Ptr, + have been moved from methods of ``convert`` to methods of ``unsafe_convert``. + + * Updated documentation at [ccall chapter] in the manual + Julia v0.3.0 Release Notes ========================== @@ -957,10 +993,12 @@ Bugfixes and performance updates Too numerous to mention. [packages chapter]: http://docs.julialang.org/en/latest/manual/packages/ +[ccall chapter]: http://docs.julialang.org/en/latest/manual/calling-c-and-fortran-code/ [sorting functions]: http://docs.julialang.org/en/latest/stdlib/sort/ [pairwise summation]: https://en.wikipedia.org/wiki/Pairwise_summation [a448e080]: https://github.com/JuliaLang/julia/commit/a448e080dc736c7fb326426dfcb2528be36973d3 [5e3f074b]: https://github.com/JuliaLang/julia/commit/5e3f074b9173044a0a4219f9b285879ff7cec041 +[Improvements to ccall and cfunction, and Ref]: #improvements-to-ccall-and-cfunction-and-ref [#13]: https://github.com/JuliaLang/julia/issues/13 [#69]: https://github.com/JuliaLang/julia/issues/69 diff --git a/doc/helpdb.jl b/doc/helpdb.jl index 8f1a6909bc479..2d6a9e9267aa3 100644 --- a/doc/helpdb.jl +++ b/doc/helpdb.jl @@ -226,8 +226,8 @@ Any[ ("Base","reinterpret","reinterpret(type, A) Change the type-interpretation of a block of memory. For example, - \"reinterpret(Float32, uint32(7))\" interprets the 4 bytes - corresponding to \"uint32(7)\" as a \"Float32\". For arrays, this + \"reinterpret(Float32, UInt32(7))\" interprets the 4 bytes + corresponding to \"UInt32(7)\" as a \"Float32\". For arrays, this constructs an array with the same binary data as the given array, but with the specified element type. @@ -422,18 +422,6 @@ Any[ "), -("Base","flipud","flipud(A) - - Equivalent to \"flipdim(A,1)\". - -"), - -("Base","fliplr","fliplr(A) - - Equivalent to \"flipdim(A,2)\". - -"), - ("Base","circshift","circshift(A, shifts) Circularly shift the data in an array. The second argument is a @@ -1968,9 +1956,9 @@ Any[ Only valid in the context of an Expr returned from a macro. Prevents the macro hygiene pass from turning embedded variables - into gensym variables. See the *Non-Standard AbstractString - Literals* section of the Metaprogramming chapter of the manual for - more details and examples. + into gensym variables. See the *Macros* section of the + Metaprogramming chapter of the manual for more details and + examples. "), @@ -2707,19 +2695,27 @@ Any[ "), -("Base","ccall","ccall((symbol, library) or fptr, RetType, (ArgType1, ...), ArgVar1, ...) +("Base","ccall","ccall((symbol, library) or function_pointer, ReturnType, (ArgumentType1, ...), ArgumentValue1, ...) Call function in C-exported shared library, specified by - \"(function name, library)\" tuple, where each component is a - AbstractString or :Symbol. Alternatively, ccall may be used to call - a function pointer returned by dlsym, but note that this usage is - generally discouraged to facilitate future static compilation. Note - that the argument type tuple must be a literal tuple, and not a - tuple-valued variable or expression. + \"(function name, library)\" tuple, where each component is an + AbstractString or :Symbol. + + Note that the argument type tuple must be a literal tuple, and not + a tuple-valued variable or expression. Alternatively, ccall may + also be used to call a function pointer, such as one returned by + dlsym. + + Each \"ArgumentValue\" to the \"ccall\" will be converted to the + corresponding \"ArgumentType\", by automatic insertion of calls to + \"unsafe_convert(ArgumentType, cconvert(ArgumentType, + ArgumentValue))\". (see also the documentation for each of these + functions for further details). In most cases, this simply results + in a call to \"convert(ArgumentType, ArgumentValue)\" "), -("Base","cglobal","cglobal((symbol, library) or ptr[, Type=Void]) +("Base","cglobal","cglobal((symbol, library)[, type=Void]) Obtain a pointer to a global variable in a C-exported shared library, specified exactly as in \"ccall\". Returns a @@ -2729,7 +2725,7 @@ Any[ "), -("Base","cfunction","cfunction(fun::Function, RetType::Type, (ArgTypes...)) +("Base","cfunction","cfunction(function::Function, ReturnType::Type, (ArgumentTypes...)) Generate C-callable function pointer from Julia function. Type annotation of the return value in the callback function is a must @@ -2748,7 +2744,7 @@ Any[ "), -("Base","dlopen","dlopen(libfile::AbstractString[, flags::Integer]) +("Base","dlopen","dlopen(library_file::AbstractString[, flags::Integer]) Load a shared library, returning an opaque handle. @@ -2767,7 +2763,7 @@ Any[ "), -("Base","dlopen_e","dlopen_e(libfile::AbstractString[, flags::Integer]) +("Base","dlopen_e","dlopen_e(library_file::AbstractString[, flags::Integer]) Similar to \"dlopen()\", except returns a \"NULL\" pointer instead of raising errors. @@ -2886,11 +2882,56 @@ Any[ Call \"realloc\" from the C standard library. + See warning in \"c_free\" documentation regarding only using this + on memory originally obtained from \"c_malloc\". + "), ("Base","c_free","c_free(addr::Ptr) - Call \"free\" from the C standard library. + Call \"free\" from the C standard library. Only use this on memory + obtained from \"c_malloc\", not on pointers retrieved from other C + libraries. \"Ptr\" objects obtained from C libraries should be + freed by the free functions defined in that library, to avoid + assertion failures if multiple \"libc\" libraries exist on the + system. + +"), + +("Base","unsafe_convert","unsafe_convert(T, x) + + Convert \"x\" to a value of type \"T\" + + In cases where \"convert\" would need to take a Julia object and + turn it into a \"Ptr\", this function should be used to define and + perform that conversion. + + Be careful to ensure that a julia reference to \"x\" exists as long + as the result of this function will be used. Accordingly, the + argument \"x\" to this function should never be an expression, only + a variable name or field reference. For example, \"x=a.b.c\" is + acceptable, but \"x=[a,b,c]\" is not. + + The \"unsafe\" prefix on this function indicates that using the + result of this function after the \"x\" argument to this function + is no longer accessible to the program may cause undefined + behavior, including program corruption or segfaults, at any later + time. + +"), + +("Base","cconvert","cconvert(T, x) + + Convert \"x\" to a value of type \"T\", typically by calling + \"convert(T,x)\" + + In cases where \"x\" cannot be safely converted to \"T\", unlike + \"convert\", \"cconvert\" may return an object of a type different + from \"T\", which however is suitable for \"unsafe_convert\" to + handle. + + Neither \"convert\" nor \"cconvert\" should take a Julia object and + turn it into a \"Ptr\". "), @@ -2900,6 +2941,11 @@ Any[ (1-indexed) starting at \"p\". This is equivalent to the C expression \"p[i-1]\". + The \"unsafe\" prefix on this function indicates that no validation + is performed on the pointer >>``<>``< Bool ("Base","reprmime","reprmime(mime, x) - Returns a \"AbstractString\" or \"Vector{UInt8}\" containing the + Returns an \"AbstractString\" or \"Vector{UInt8}\" containing the representation of \"x\" in the requested \"mime\" type, as written by \"writemime\" (throwing a \"MethodError\" if no appropriate - \"writemime\" is available). A \"AbstractString\" is returned for + \"writemime\" is available). An \"AbstractString\" is returned for MIME types with textual representations (such as \"\"text/html\"\" or \"\"application/postscript\"\"), whereas binary data is returned as \"Vector{UInt8}\". (The function \"istext(mime)\" returns whether or not Julia treats a given \"mime\" type as text.) - As a special case, if \"x\" is a \"AbstractString\" (for textual + As a special case, if \"x\" is an \"AbstractString\" (for textual MIME types) or a \"Vector{UInt8}\" (for binary MIME types), the \"reprmime\" function assumes that \"x\" is already in the requested \"mime\" format and simply returns \"x\". @@ -6278,9 +6361,10 @@ displayable(d::Display, mime) -> Bool ("Base","stringmime","stringmime(mime, x) - Returns a \"AbstractString\" containing the representation of \"x\" - in the requested \"mime\" type. This is similar to \"reprmime\" - except that binary data is base64-encoded as an ASCII string. + Returns an \"AbstractString\" containing the representation of + \"x\" in the requested \"mime\" type. This is similar to + \"reprmime\" except that binary data is base64-encoded as an ASCII + string. "), @@ -10523,36 +10607,6 @@ popdisplay(d::Display) "), -("Base","bool","bool(x) - - Convert a number or numeric array to boolean - -"), - -("Base","int","int(x) - - Convert a number or array to the default integer type on your - platform. Alternatively, \"x\" can be a string, which is parsed as - an integer. - -"), - -("Base","uint","uint(x) - - Convert a number or array to the default unsigned integer type on - your platform. Alternatively, \"x\" can be a string, which is - parsed as an unsigned integer. - -"), - -("Base","integer","integer(x) - - Convert a number or array to integer type. If \"x\" is already of - integer type it is unchanged, otherwise it converts it to the - default integer type on your platform. - -"), - ("Base","signed","signed(x) Convert a number to a signed integer. If the argument is unsigned, @@ -10568,84 +10622,6 @@ popdisplay(d::Display) "), -("Base","int8","int8(x) - - Convert a number or array to \"Int8\" data type - -"), - -("Base","int16","int16(x) - - Convert a number or array to \"Int16\" data type - -"), - -("Base","int32","int32(x) - - Convert a number or array to \"Int32\" data type - -"), - -("Base","int64","int64(x) - - Convert a number or array to \"Int64\" data type - -"), - -("Base","int128","int128(x) - - Convert a number or array to \"Int128\" data type - -"), - -("Base","uint8","uint8(x) - - Convert a number or array to \"UInt8\" data type - -"), - -("Base","uint16","uint16(x) - - Convert a number or array to \"UInt16\" data type - -"), - -("Base","uint32","uint32(x) - - Convert a number or array to \"UInt32\" data type - -"), - -("Base","uint64","uint64(x) - - Convert a number or array to \"UInt64\" data type - -"), - -("Base","uint128","uint128(x) - - Convert a number or array to \"UInt128\" data type - -"), - -("Base","float16","float16(x) - - Convert a number or array to \"Float16\" data type - -"), - -("Base","float32","float32(x) - - Convert a number or array to \"Float32\" data type - -"), - -("Base","float64","float64(x) - - Convert a number or array to \"Float64\" data type - -"), - ("Base","float32_isvalid","float32_isvalid(x, out::Vector{Float32}) -> Bool Convert a number or array to \"Float32\" data type, returning true @@ -10668,9 +10644,6 @@ popdisplay(d::Display) type. For numeric data, the smallest suitable \"FloatingPoint\" type is used. Converts strings to \"Float64\". - This function is not recommended for arrays. It is better to use a - more specific function such as \"float32\" or \"float64\". - "), ("Base","significand","significand(x) @@ -10692,32 +10665,12 @@ popdisplay(d::Display) "), -("Base","complex64","complex64(r[, i]) - - Convert to \"r + i*im\" represented as a \"Complex64\" data type. - \"i\" defaults to zero. - -"), - -("Base","complex128","complex128(r[, i]) - - Convert to \"r + i*im\" represented as a \"Complex128\" data type. - \"i\" defaults to zero. - -"), - ("Base","complex","complex(r[, i]) Convert real numbers or arrays to complex. \"i\" defaults to zero. "), -("Base","char","char(x) - - Convert a number or array to \"Char\" data type - -"), - ("Base","bswap","bswap(n) Byte-swap an integer @@ -10937,7 +10890,7 @@ popdisplay(d::Display) ("Base","BigInt","BigInt(x) Create an arbitrary precision integer. \"x\" may be an \"Int\" (or - anything that can be converted to an \"Int\") or a + anything that can be converted to an \"Int\") or an \"AbstractString\". The usual mathematical operators are defined for this type, and results are promoted to a \"BigInt\". @@ -10946,7 +10899,7 @@ popdisplay(d::Display) ("Base","BigFloat","BigFloat(x) Create an arbitrary precision floating point number. \"x\" may be - an \"Integer\", a \"Float64\", a \"AbstractString\" or a + an \"Integer\", a \"Float64\", an \"AbstractString\" or a \"BigInt\". The usual mathematical operators are defined for this type, and results are promoted to a \"BigFloat\". Note that because floating-point numbers are not exactly-representable in decimal @@ -11000,7 +10953,7 @@ popdisplay(d::Display) Number of zeros in the binary representation of \"x\". - julia> count_zeros(int32(2 ^ 16 - 1)) + julia> count_zeros(Int32(2 ^ 16 - 1)) 16 "), @@ -11009,7 +10962,7 @@ popdisplay(d::Display) Number of zeros leading the binary representation of \"x\". - julia> leading_zeros(int32(1)) + julia> leading_zeros(Int32(1)) 31 "), @@ -11018,7 +10971,7 @@ popdisplay(d::Display) Number of ones leading the binary representation of \"x\". - julia> leading_ones(uint32(2 ^ 32 - 2)) + julia> leading_ones(UInt32(2 ^ 32 - 2)) 31 "), diff --git a/doc/stdlib/c.rst b/doc/stdlib/c.rst index 86b15018d86c8..7a65389eb9b10 100644 --- a/doc/stdlib/c.rst +++ b/doc/stdlib/c.rst @@ -6,9 +6,16 @@ .. function:: ccall((symbol, library) or function_pointer, ReturnType, (ArgumentType1, ...), ArgumentValue1, ...) - Call function in C-exported shared library, specified by ``(function name, library)`` tuple, where each component is an ``AbstractString`` or ``Symbol``. Alternatively, - ccall may be used to call a function pointer returned by dlsym, but note that this usage is generally discouraged to facilitate future static compilation. + Call function in C-exported shared library, specified by ``(function name, library)`` tuple, + where each component is an AbstractString or :Symbol. + Note that the argument type tuple must be a literal tuple, and not a tuple-valued variable or expression. + Alternatively, ccall may also be used to call a function pointer, such as one returned by dlsym. + + Each ``ArgumentValue`` to the ``ccall`` will be converted to the corresponding ``ArgumentType``, + by automatic insertion of calls to ``unsafe_convert(ArgumentType, cconvert(ArgumentType, ArgumentValue))``. + (see also the documentation for each of these functions for further details). + In most cases, this simply results in a call to ``convert(ArgumentType, ArgumentValue)`` .. function:: cglobal((symbol, library) [, type=Void]) @@ -118,30 +125,78 @@ Call ``realloc`` from the C standard library. + See warning in ``c_free`` documentation regarding only using this on memory originally obtained from ``c_malloc``. + .. function:: c_free(addr::Ptr) - Call ``free`` from the C standard library. + Call ``free`` from the C standard library. Only use this on memory obtained from ``c_malloc``, + not on pointers retrieved from other C libraries. + ``Ptr`` objects obtained from C libraries should be freed by the free functions defined in that library, + to avoid assertion failures if multiple ``libc`` libraries exist on the system. + +.. function:: unsafe_convert(T,x) + + Convert "x" to a value of type "T" + + In cases where ``convert`` would need to take a Julia object and turn it into a ``Ptr``, + this function should be used to define and perform that conversion. + + Be careful to ensure that a julia reference to ``x`` exists as long as the result of this function will be used. + Accordingly, the argument ``x`` to this function should never be an expression, + only a variable name or field reference. + For example, ``x=a.b.c`` is acceptable, but ``x=[a,b,c]`` is not. + + The ``unsafe`` prefix on this function indicates that using the result of this function + after the ``x`` argument to this function is no longer accessible to the program may cause + undefined behavior, including program corruption or segfaults, at any later time. + +.. function:: cconvert(T,x) + + Convert "x" to a value of type "T", typically by calling ``convert(T,x)`` + + In cases where "x" cannot be safely converted to "T", unlike ``convert``, + ``cconvert`` may return an object of a type different from "T", + which however is suitable for ``unsafe_convert`` to handle. + + Neither ``convert`` nor ``cconvert`` should take a Julia object and turn it into a ``Ptr``. .. function:: unsafe_load(p::Ptr{T},i::Integer) + Load a value of type ``T`` from the address of the ith element (1-indexed) starting at ``p``. This is equivalent to the C expression ``p[i-1]``. + The ``unsafe`` prefix on this function indicates that no validation is performed + on the pointer ``p` to ensure that it is valid. Incorrect usage may segfault + your program or return garbage answers, in the same manner as C. + .. function:: unsafe_store!(p::Ptr{T},x,i::Integer) Store a value of type ``T`` to the address of the ith element (1-indexed) starting at ``p``. This is equivalent to the C expression ``p[i-1] = x``. + The ``unsafe`` prefix on this function indicates that no validation is performed + on the pointer ``p` to ensure that it is valid. Incorrect usage may corrupt or segfault + your program, in the same manner as C. + .. function:: unsafe_copy!(dest::Ptr{T}, src::Ptr{T}, N) Copy ``N`` elements from a source pointer to a destination, with no checking. The size of an element is determined by the type of the pointers. + The ``unsafe`` prefix on this function indicates that no validation is performed + on the pointers ``dest`` and ``src`` to ensure that they are valid. + Incorrect usage may corrupt or segfault your program, in the same manner as C. + .. function:: unsafe_copy!(dest::Array, do, src::Array, so, N) Copy ``N`` elements from a source array to a destination, starting at offset ``so`` in the source and ``do`` in the destination (1-indexed). + The ``unsafe`` prefix on this function indicates that no validation is performed + to ensure that N is inbounds on either array. Incorrect usage may corrupt or segfault + your program, in the same manner as C. + .. function:: copy!(dest, src) Copy all elements from collection ``src`` to array ``dest``. Returns ``dest``. @@ -155,7 +210,9 @@ Get the native address of an array or string element. Be careful to ensure that a julia reference to ``a`` exists as long as this - pointer will be used. + pointer will be used. This function is "unsafe" like ``unsafe_convert``. + + Calling ``Ref(array[, index])`` is generally preferable to this function. .. function:: pointer_to_array(pointer, dims[, take_ownership::Bool]) From c26bccf67d50be61eadb1d4c2c6ada19a13420e2 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sun, 8 Mar 2015 15:51:05 -0400 Subject: [PATCH 13/14] minor doc cleanup --- LICENSE.md | 6 ++++-- doc/manual/calling-c-and-fortran-code.rst | 11 +++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 75812c87bc712..64434592a5f6e 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -27,6 +27,10 @@ for exceptions. > OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION > WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Julia includes code from the following projects, which have their own licenses: +- [LDC](https://github.com/ldc-developers/ldc/blob/master/LICENSE) (for ccall/cfunction ABI definitions) +- [MUSL](http://git.musl-libc.org/cgit/musl/tree/COPYRIGHT) (for getopt implementations on Windows) +- [NetBSD](http://www.netbsd.org/about/redistribution.html) (for setjmp/longjmp implementations on Windows) The Julia language links to the following external libraries, which have their own licenses: @@ -52,7 +56,6 @@ their own licenses: - [GMP](http://gmplib.org/manual/Copying.html#Copying) - [LIBGIT2](https://github.com/libgit2/libgit2/blob/development/COPYING) - [MPFR](http://www.mpfr.org/mpfr-current/mpfr.html#Copying) -- [MUSL](http://git.musl-libc.org/cgit/musl/tree/COPYRIGHT) - [OPENBLAS](https://raw.github.com/xianyi/OpenBLAS/master/LICENSE) - [LAPACK](http://netlib.org/lapack/LICENSE.txt) - [PCRE](http://www.pcre.org/licence.txt) @@ -70,7 +73,6 @@ The following components of Julia's standard library have separate licenses: Julia builds the following libraries by default, but does not use them itself: - [RMATH](http://www.r-project.org/Licenses/) -- [LDC](https://github.com/ldc-developers/ldc/blob/master/LICENSE) Julia's build process uses the following external tools: diff --git a/doc/manual/calling-c-and-fortran-code.rst b/doc/manual/calling-c-and-fortran-code.rst index 94ee2e897c6c1..a645619ba00a3 100644 --- a/doc/manual/calling-c-and-fortran-code.rst +++ b/doc/manual/calling-c-and-fortran-code.rst @@ -152,11 +152,14 @@ uninitialized array and passing a pointer to its data to the C function. Creating C-Compatible Julia Function Pointers --------------------------------------------- -It is possible to pass Julia functions to native functions that accept function -pointer arguments by creating function pointers via the `cfunction` function. +It is possible to pass Julia functions to native c-functions that accept +function pointer arguments. For example, to match c-prototypes of the form:: -Finally, you can use ``cfunction`` to actually generate a call to the -Julia library function. Arguments to ``cfunction`` are as follows: + typedef returntype (*functiontype)(argumenttype,...) + +The function `cfunction` generates the c-compatible function pointer for +a call to a Julia Julia library function. +Arguments to ``cfunction`` are as follows: 1. A Julia Function From 22490c1a6aaa6a12b60bd64554844de5034d153e Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 9 Mar 2015 20:04:54 -0400 Subject: [PATCH 14/14] copy LDC license directly into abi implementation files --- src/abi_llvm.cpp | 31 ++++++++++++++++++++++++++++--- src/abi_win32.cpp | 29 +++++++++++++++++++++++++++-- src/abi_win64.cpp | 29 +++++++++++++++++++++++++++-- src/abi_x86.cpp | 29 +++++++++++++++++++++++++++-- src/abi_x86_64.cpp | 29 +++++++++++++++++++++++++++-- src/ccall.cpp | 7 +++++-- 6 files changed, 141 insertions(+), 13 deletions(-) diff --git a/src/abi_llvm.cpp b/src/abi_llvm.cpp index 9cd6404bd2fc2..2927e9769d18b 100644 --- a/src/abi_llvm.cpp +++ b/src/abi_llvm.cpp @@ -2,8 +2,33 @@ // // LDC – the LLVM D compiler // -// This file is distributed under the BSD-style LDC license. See the LICENSE -// file for details. +// This file is distributed under the BSD-style LDC license: +// +// Copyright (c) 2007-2012 LDC Team. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the LDC Team nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //===----------------------------------------------------------------------===// // @@ -22,7 +47,7 @@ bool use_sret(AbiState *state,jl_value_t *ty) return false; } -void needPassByRef(AbiState *state,jl_value_t *ty, bool *byRef, bool *inReg) +void needPassByRef(AbiState *state,jl_value_t *ty, bool *byRef, bool *inReg, bool *byRefAttr) { return; } diff --git a/src/abi_win32.cpp b/src/abi_win32.cpp index 0899638b74304..b7949d5aaa68a 100644 --- a/src/abi_win32.cpp +++ b/src/abi_win32.cpp @@ -2,8 +2,33 @@ // // LDC – the LLVM D compiler // -// This file is distributed under the BSD-style LDC license. See the LICENSE -// file for details. +// This file is distributed under the BSD-style LDC license: +// +// Copyright (c) 2007-2012 LDC Team. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the LDC Team nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //===----------------------------------------------------------------------===// // diff --git a/src/abi_win64.cpp b/src/abi_win64.cpp index a586e609cda98..8f19b1c310a85 100644 --- a/src/abi_win64.cpp +++ b/src/abi_win64.cpp @@ -2,8 +2,33 @@ // // LDC – the LLVM D compiler // -// This file is distributed under the BSD-style LDC license. See the LICENSE -// file for details. +// This file is distributed under the BSD-style LDC license: +// +// Copyright (c) 2007-2012 LDC Team. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the LDC Team nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //===----------------------------------------------------------------------===// // diff --git a/src/abi_x86.cpp b/src/abi_x86.cpp index 49563b7e7a759..0cae279eb069c 100644 --- a/src/abi_x86.cpp +++ b/src/abi_x86.cpp @@ -2,8 +2,33 @@ // // LDC – the LLVM D compiler // -// This file is distributed under the BSD-style LDC license. See the LICENSE -// file for details. +// This file is distributed under the BSD-style LDC license: +// +// Copyright (c) 2007-2012 LDC Team. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the LDC Team nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //===----------------------------------------------------------------------===// // diff --git a/src/abi_x86_64.cpp b/src/abi_x86_64.cpp index 220e4b3add5a0..81735c196dd80 100644 --- a/src/abi_x86_64.cpp +++ b/src/abi_x86_64.cpp @@ -2,8 +2,33 @@ // // LDC – the LLVM D compiler // -// This file is distributed under the BSD-style LDC license. See the LICENSE -// file for details. +// This file is distributed under the BSD-style LDC license: +// +// Copyright (c) 2007-2012 LDC Team. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the LDC Team nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //===----------------------------------------------------------------------===// // diff --git a/src/ccall.cpp b/src/ccall.cpp index d2237640a81d8..cd0cca668eb27 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -223,18 +223,21 @@ static Value *runtime_sym_lookup(PointerType *funcptype, char *f_lib, char *f_na #if defined ABI_LLVM # include "abi_llvm.cpp" -#elif defined _P64 +#elif defined _CPU_X86_64_ # if defined _OS_WINDOWS_ # include "abi_win64.cpp" # else # include "abi_x86_64.cpp" # endif -#else +#elif defined _CPU_X86_ # if defined _OS_WINDOWS_ # include "abi_win32.cpp" # else # include "abi_x86.cpp" # endif +#else +# warning "ccall is defaulting to llvm ABI, since no platform ABI has been defined for this CPU/OS combination" +# include "abi_llvm.cpp" #endif Value *llvm_type_rewrite(Value *v, Type *target_type, jl_value_t *ty, bool isret)