Skip to content

Commit

Permalink
Add back llvmcall
Browse files Browse the repository at this point in the history
  • Loading branch information
Keno committed Jul 18, 2014
1 parent 8a454ca commit 3bce3ea
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 2 deletions.
2 changes: 1 addition & 1 deletion base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export
JULIA_HOME, nothing, Main,
# intrinsics module
Intrinsics
#ccall, cglobal, abs_float, add_float, add_int, and_int, ashr_int,
#ccall, cglobal, llvmcall, abs_float, add_float, add_int, and_int, ashr_int,
#box, bswap_int, checked_fptosi, checked_fptoui, checked_sadd,
#checked_smul, checked_ssub, checked_uadd, checked_umul, checked_usub,
#checked_trunc_sint, checked_trunc_uint,
Expand Down
4 changes: 4 additions & 0 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ t_func[nan_dom_err] = (2, 2, (a, b)->a)
t_func[eval(Core.Intrinsics,:ccall)] =
(3, Inf, (fptr, rt, at, a...)->(is(rt,Type{Void}) ? Nothing :
isType(rt) ? rt.parameters[1] : Any))
t_func[eval(Core.Intrinsics,:llvmcall)] =
(3, Inf, (fptr, rt, at, a...)->(is(rt,Type{Void}) ? Nothing :
isType(rt) ? rt.parameters[1] :
isa(rt,Tuple) ? map(x->x.parameters[1],rt) : Any))
t_func[eval(Core.Intrinsics,:cglobal)] =
(1, 2, (fptr, t...)->(isempty(t) ? Ptr{Void} :
isType(t[1]) ? Ptr{t[1].parameters[1]} : Ptr))
Expand Down
132 changes: 132 additions & 0 deletions src/ccall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,138 @@ static Value *emit_cglobal(jl_value_t **args, size_t nargs, jl_codectx_t *ctx)
return mark_julia_type(res, rt);
}

// llvmcall(ir, (rettypes...), (argtypes...), args...)
static Value *emit_llvmcall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx)
{
JL_NARGSV(llvmcall, 3)
jl_value_t *rt = NULL, *at = NULL;
JL_GC_PUSH2(&rt, &at);
{
JL_TRY {
at = jl_interpret_toplevel_expr_in(ctx->module, args[3],
&jl_tupleref(ctx->sp,0),
jl_tuple_len(ctx->sp)/2);
}
JL_CATCH {
jl_rethrow_with_add("error interpreting llvmcall return type");
}
}
{
JL_TRY {
rt = jl_interpret_toplevel_expr_in(ctx->module, args[2],
&jl_tupleref(ctx->sp,0),
jl_tuple_len(ctx->sp)/2);
}
JL_CATCH {
jl_rethrow_with_add("error interpreting ccall argument tuple");
}
}
JL_TYPECHK(llvmcall, type, rt);
JL_TYPECHK(llvmcall, tuple, at);
JL_TYPECHK(llvmcall, type, at);
int i = 1;
// Make sure to find a unique name
std::string ir_name;
while(true) {
std::stringstream name;
name << (ctx->f->getName().str()) << i++;
ir_name = name.str();
if(jl_Module->getFunction(ir_name) == NULL)
break;
}
jl_value_t *ir = static_eval(args[1], ctx, true);
if (!jl_is_byte_string(ir))
{
jl_error("First argument to llvmcall must be a string");
}

std::stringstream ir_stream;

// Generate arguments
std::string arguments;
llvm::raw_string_ostream argstream(arguments);
jl_tuple_t *tt = (jl_tuple_t*)at;
size_t nargt = jl_tuple_len(tt);
Value *argvals[nargt];

This comment has been minimized.

Copy link
@tkelman

tkelman Aug 15, 2014

Contributor

Can this be replaced by Value **argvals = (Value**) alloca(nargt*sizeof(Value*)); ? The VLA is a GCC extension that MSVC doesn't like.

This comment has been minimized.

Copy link
@Keno

Keno Aug 15, 2014

Author Member

That sounds fine.

/*
* Semantics for arguments are as follows:
* If the argument type is immutable (including bitstype), we pass the loaded llvm value
* type. Otherwise we pass a pointer to a jl_value_t.
*/
for (size_t i = 0; i < nargt; ++i)
{
jl_value_t *tti = jl_tupleref(tt,i);
Type *t = julia_type_to_llvm(tti);
t->print(argstream);
argstream << " ";
jl_value_t *argi = args[4+i];
Value *arg;
bool needroot = false;
if (t == jl_pvalue_llvmt || !jl_isbits(tti)) {
arg = emit_expr(argi, ctx, true);
if (t == jl_pvalue_llvmt && arg->getType() != jl_pvalue_llvmt) {
arg = boxed(arg, ctx);
needroot = true;
}
}
else {
arg = emit_unboxed(argi, ctx);
if (jl_is_bitstype(expr_type(argi, ctx))) {
arg = emit_unbox(t, arg, tti);
}
}

#ifdef JL_GC_MARKSWEEP
// make sure args are rooted
if (t == jl_pvalue_llvmt && (needroot || might_need_root(argi))) {
make_gcroot(arg, ctx);
}
#endif
bool mightNeedTempSpace = false;
argvals[i] = julia_to_native(t,tti,arg,argi,false,i,ctx,&mightNeedTempSpace,&mightNeedTempSpace);
if ((i+1) != nargt)
argstream << ",";
}

Type *rettype;
std::string rstring;
llvm::raw_string_ostream rtypename(rstring);
// Construct return type
rettype = julia_type_to_llvm(rt);
rettype->print(rtypename);

ir_stream << "; Number of arguments: " << nargt << "\n"
<< "define "<<rtypename.str()<<" @" << ir_name << "("<<argstream.str()<<") {\n"
<< jl_string_data(ir) << "\n}";
SMDiagnostic Err = SMDiagnostic();
std::string ir_string = ir_stream.str();
Module *m = ParseAssemblyString(ir_string.data(),jl_Module,Err,jl_LLVMContext);
if (m == NULL) {
std::string message = "Failed to parse LLVM Assembly: \n";
llvm::raw_string_ostream stream(message);
Err.print("julia",stream,true);
jl_error(stream.str().c_str());
}
Function *f = m->getFunction(ir_name);
/*
* It might be tempting to just try to set the Always inline attribute on the function
* and hope for the best. However, this doesn't work since that would require an inlining
* pass (which is a Call Graph pass and cannot be managed by a FunctionPassManager). Instead
* We are sneaky and call the inliner directly. This however doesn't work until we've actually
* generated the entire function, so we need to store it in the context until the end of the
* function. This also has the benefit of looking exactly like we cut/pasted it in in `code_llvm`.
*/
f->setLinkage(GlobalValue::LinkOnceODRLinkage);

// the actual call
CallInst *inst = builder.CreateCall(f,ArrayRef<Value*>(&argvals[0],nargt));
ctx->to_inline.push_back(inst);

JL_GC_POP();

return mark_julia_type(inst,rt);
}

// --- code generator for ccall itself ---

// ccall(pointer, rettype, (argtypes...), args...)
Expand Down
4 changes: 3 additions & 1 deletion src/intrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ namespace JL_I {
// pointer access
pointerref, pointerset, pointertoref,
// c interface
ccall, cglobal, jl_alloca
ccall, cglobal, jl_alloca, llvmcall
};
};

Expand Down Expand Up @@ -820,6 +820,7 @@ static Value *emit_intrinsic(intrinsic f, jl_value_t **args, size_t nargs,
switch (f) {
case ccall: return emit_ccall(args, nargs, ctx);
case cglobal: return emit_cglobal(args, nargs, ctx);
case llvmcall: return emit_llvmcall(args, nargs, ctx);

HANDLE(box,2) return generic_box(args[1], args[2], ctx);
HANDLE(unbox,2) return generic_unbox(args[1], args[2], ctx);
Expand Down Expand Up @@ -1438,4 +1439,5 @@ extern "C" void jl_init_intrinsic_functions(void)
ADD_I(nan_dom_err);
ADD_I(ccall); ADD_I(cglobal);
ADD_I(jl_alloca);
ADD_I(llvmcall);
}

0 comments on commit 3bce3ea

Please sign in to comment.