Esempio n. 1
0
static LLVMValueRef declare_ffi(compile_t* c, const char* f_name,
  reach_type_t* t, ast_t* args, bool intrinsic)
{
  ast_t* last_arg = ast_childlast(args);

  if((last_arg != NULL) && (ast_id(last_arg) == TK_ELLIPSIS))
    return declare_ffi_vararg(c, f_name, t);

  int count = (int)ast_childcount(args);
  size_t buf_size = count * sizeof(LLVMTypeRef);
  LLVMTypeRef* f_params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
  count = 0;

  ast_t* arg = ast_child(args);

  deferred_reification_t* reify = c->frame->reify;

  while(arg != NULL)
  {
    ast_t* p_type = ast_type(arg);

    if(p_type == NULL)
      p_type = ast_childidx(arg, 1);

    p_type = deferred_reify(reify, p_type, c->opt);
    reach_type_t* pt = reach_type(c->reach, p_type);
    pony_assert(pt != NULL);
    f_params[count++] = ((compile_type_t*)pt->c_type)->use_type;
    ast_free_unattached(p_type);
    arg = ast_sibling(arg);
  }

  LLVMTypeRef r_type = ffi_return_type(c, t, intrinsic);
  LLVMTypeRef f_type = LLVMFunctionType(r_type, f_params, count, false);
  LLVMValueRef func = LLVMAddFunction(c->module, f_name, f_type);

  ponyint_pool_free_size(buf_size, f_params);
  return func;
}
Esempio n. 2
0
static LLVMValueRef declare_ffi(compile_t* c, const char* f_name,
  reach_type_t* t, ast_t* args, bool err, bool intrinsic)
{
  ast_t* last_arg = ast_childlast(args);

  if((last_arg != NULL) && (ast_id(last_arg) == TK_ELLIPSIS))
    return declare_ffi_vararg(c, f_name, t, err);

  int count = (int)ast_childcount(args);
  size_t buf_size = count * sizeof(LLVMTypeRef);
  LLVMTypeRef* f_params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
  count = 0;

  ast_t* arg = ast_child(args);

  while(arg != NULL)
  {
    ast_t* p_type = ast_type(arg);

    if(p_type == NULL)
      p_type = ast_childidx(arg, 1);

    reach_type_t* pt = reach_type(c->reach, p_type);
    pony_assert(pt != NULL);

    // An intrinsic that takes a Bool should be i1, not ibool.
    if(intrinsic && is_bool(pt->ast))
      f_params[count++] = c->i1;
    else
      f_params[count++] = pt->use_type;

    arg = ast_sibling(arg);
  }

  LLVMTypeRef r_type;

  if(t->underlying == TK_TUPLETYPE)
  {
    // Can't use the named type. Build an unnamed type with the same
    // elements.
    unsigned int count = LLVMCountStructElementTypes(t->use_type);
    size_t buf_size = count * sizeof(LLVMTypeRef);
    LLVMTypeRef* e_types = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
    LLVMGetStructElementTypes(t->use_type, e_types);

    if(intrinsic)
    {
      ast_t* child = ast_child(t->ast);
      size_t i = 0;

      while(child != NULL)
      {
        // A Bool in an intrinsic tuple return type is an i1, not an ibool.
        if(is_bool(child))
          e_types[i] = c->i1;

        child = ast_sibling(child);
        i++;
      }
    }

    r_type = LLVMStructTypeInContext(c->context, e_types, count, false);
    ponyint_pool_free_size(buf_size, e_types);
  } else {
    // An intrinsic that returns a Bool returns an i1, not an ibool.
    if(intrinsic && is_bool(t->ast))
      r_type = c->i1;
    else
      r_type = t->use_type;
  }

  LLVMTypeRef f_type = LLVMFunctionType(r_type, f_params, count, false);
  LLVMValueRef func = LLVMAddFunction(c->module, f_name, f_type);

  if(!err)
  {
#if PONY_LLVM >= 309
    LLVM_DECLARE_ATTRIBUTEREF(nounwind_attr, nounwind, 0);

    LLVMAddAttributeAtIndex(func, LLVMAttributeFunctionIndex, nounwind_attr);
#else
    LLVMAddFunctionAttr(func, LLVMNoUnwindAttribute);
#endif
  }

  ponyint_pool_free_size(buf_size, f_params);
  return func;
}
Esempio n. 3
0
LLVMValueRef gen_ffi(compile_t* c, ast_t* ast)
{
  AST_GET_CHILDREN(ast, id, typeargs, args, named_args, can_err);
  bool err = (ast_id(can_err) == TK_QUESTION);

  // Get the function name, +1 to skip leading @
  const char* f_name = ast_name(id) + 1;

  // Get the return type.
  ast_t* type = ast_type(ast);
  reach_type_t* t = reach_type(c->reach, type);
  pony_assert(t != NULL);

  // Get the function.
  LLVMValueRef func = LLVMGetNamedFunction(c->module, f_name);

  if(func == NULL)
  {
    // If we have no prototype, declare one.
    ast_t* decl = (ast_t*)ast_data(ast);

    if(decl != NULL)
    {
      // Define using the declared types.
      AST_GET_CHILDREN(decl, decl_id, decl_ret, decl_params, decl_err);
      err = (ast_id(decl_err) == TK_QUESTION);
      func = declare_ffi(c, f_name, t, decl_params, err, false);
    } else if(!strncmp(f_name, "llvm.", 5)) {
      // Intrinsic, so use the exact types we supply.
      func = declare_ffi(c, f_name, t, args, err, true);
    } else {
      // Make it varargs.
      func = declare_ffi_vararg(c, f_name, t, err);
    }
  }

  // Generate the arguments.
  int count = (int)ast_childcount(args);
  size_t buf_size = count * sizeof(LLVMValueRef);
  LLVMValueRef* f_args = (LLVMValueRef*)ponyint_pool_alloc_size(buf_size);

  LLVMTypeRef f_type = LLVMGetElementType(LLVMTypeOf(func));
  LLVMTypeRef* f_params = NULL;
  bool vararg = (LLVMIsFunctionVarArg(f_type) != 0);

  if(!vararg)
  {
    f_params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
    LLVMGetParamTypes(f_type, f_params);
  }

  ast_t* arg = ast_child(args);

  for(int i = 0; i < count; i++)
  {
    f_args[i] = gen_expr(c, arg);

    if(!vararg)
      f_args[i] = cast_ffi_arg(c, f_args[i], f_params[i]);

    if(f_args[i] == NULL)
    {
      ponyint_pool_free_size(buf_size, f_args);
      return NULL;
    }

    arg = ast_sibling(arg);
  }

  // If we can error out and we have an invoke target, generate an invoke
  // instead of a call.
  LLVMValueRef result;
  codegen_debugloc(c, ast);

  if(err && (c->frame->invoke_target != NULL))
    result = invoke_fun(c, func, f_args, count, "", false);
  else
    result = LLVMBuildCall(c->builder, func, f_args, count, "");

  codegen_debugloc(c, NULL);
  ponyint_pool_free_size(buf_size, f_args);

  if(!vararg)
    ponyint_pool_free_size(buf_size, f_params);

  // Special case a None return value, which is used for void functions.
  if(is_none(type))
    return t->instance;

  return result;
}
Esempio n. 4
0
static LLVMValueRef declare_ffi(compile_t* c, const char* f_name,
  gentype_t* g, ast_t* args, bool err)
{
  ast_t* last_arg = ast_childlast(args);

  if((last_arg != NULL) && (ast_id(last_arg) == TK_ELLIPSIS))
    return declare_ffi_vararg(c, f_name, g, err);

  int count = (int)ast_childcount(args);
  size_t buf_size = count * sizeof(LLVMTypeRef);
  LLVMTypeRef* f_params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
  count = 0;

  ast_t* arg = ast_child(args);

  while(arg != NULL)
  {
    ast_t* p_type = ast_type(arg);

    if(p_type == NULL)
      p_type = ast_childidx(arg, 1);

    gentype_t param_g;

    if(!gentype(c, p_type, &param_g))
      return NULL;

    f_params[count++] = param_g.use_type;
    arg = ast_sibling(arg);
  }

  // We may have generated the function by generating a parameter type.
  LLVMValueRef func = LLVMGetNamedFunction(c->module, f_name);

  if(func == NULL)
  {
    LLVMTypeRef r_type;

    if(g->underlying == TK_TUPLETYPE)
    {
      // Can't use the named type. Build an unnamed type with the same
      // elements.
      unsigned int count = LLVMCountStructElementTypes(g->use_type);
      size_t buf_size = count * sizeof(LLVMTypeRef);
      LLVMTypeRef* e_types = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
      LLVMGetStructElementTypes(g->use_type, e_types);
      r_type = LLVMStructTypeInContext(c->context, e_types, count, false);
      ponyint_pool_free_size(buf_size, e_types);
    } else {
      r_type = g->use_type;
    }

    LLVMTypeRef f_type = LLVMFunctionType(r_type, f_params, count, false);
    func = LLVMAddFunction(c->module, f_name, f_type);

    if(!err)
      LLVMAddFunctionAttr(func, LLVMNoUnwindAttribute);
  }

  ponyint_pool_free_size(buf_size, f_params);
  return func;
}
Esempio n. 5
0
LLVMValueRef gen_ffi(compile_t* c, ast_t* ast)
{
  AST_GET_CHILDREN(ast, id, typeargs, args, named_args, can_err);
  bool err = (ast_id(can_err) == TK_QUESTION);

  // Get the function name, +1 to skip leading @
  const char* f_name = ast_name(id) + 1;

  deferred_reification_t* reify = c->frame->reify;

  // Get the return type.
  ast_t* type = deferred_reify(reify, ast_type(ast), c->opt);
  reach_type_t* t = reach_type(c->reach, type);
  pony_assert(t != NULL);
  ast_free_unattached(type);

  // Get the function. First check if the name is in use by a global and error
  // if it's the case.
  ffi_decl_t* ffi_decl;
  bool is_func = false;
  LLVMValueRef func = LLVMGetNamedGlobal(c->module, f_name);

  if(func == NULL)
  {
    func = LLVMGetNamedFunction(c->module, f_name);
    is_func = true;
  }

  if(func == NULL)
  {
    // If we have no prototype, declare one.
    ast_t* decl = (ast_t*)ast_data(ast);

    if(decl != NULL)
    {
      // Define using the declared types.
      AST_GET_CHILDREN(decl, decl_id, decl_ret, decl_params, decl_err);
      err = (ast_id(decl_err) == TK_QUESTION);
      func = declare_ffi(c, f_name, t, decl_params, false);
    } else if(!strncmp(f_name, "llvm.", 5) || !strncmp(f_name, "internal.", 9)) {
      // Intrinsic, so use the exact types we supply.
      func = declare_ffi(c, f_name, t, args, true);
    } else {
      // Make it varargs.
      func = declare_ffi_vararg(c, f_name, t);
    }

    size_t index = HASHMAP_UNKNOWN;

#ifndef PONY_NDEBUG
    ffi_decl_t k;
    k.func = func;

    ffi_decl = ffi_decls_get(&c->ffi_decls, &k, &index);
    pony_assert(ffi_decl == NULL);
#endif

    ffi_decl = POOL_ALLOC(ffi_decl_t);
    ffi_decl->func = func;
    ffi_decl->decl = (decl != NULL) ? decl : ast;

    ffi_decls_putindex(&c->ffi_decls, ffi_decl, index);
  } else {
    ffi_decl_t k;
    k.func = func;
    size_t index = HASHMAP_UNKNOWN;

    ffi_decl = ffi_decls_get(&c->ffi_decls, &k, &index);

    if((ffi_decl == NULL) && (!is_func || LLVMHasMetadataStr(func, "pony.abi")))
    {
      ast_error(c->opt->check.errors, ast, "cannot use '%s' as an FFI name: "
        "name is already in use by the internal ABI", f_name);
      return NULL;
    }

    pony_assert(is_func);
  }

  // Generate the arguments.
  int count = (int)ast_childcount(args);
  size_t buf_size = count * sizeof(LLVMValueRef);
  LLVMValueRef* f_args = (LLVMValueRef*)ponyint_pool_alloc_size(buf_size);

  LLVMTypeRef f_type = LLVMGetElementType(LLVMTypeOf(func));
  LLVMTypeRef* f_params = NULL;
  bool vararg = (LLVMIsFunctionVarArg(f_type) != 0);

  if(!vararg)
  {
    if(count != (int)LLVMCountParamTypes(f_type))
    {
      ast_error(c->opt->check.errors, ast,
        "conflicting declarations for FFI function: declarations have an "
        "incompatible number of parameters");

      if(ffi_decl != NULL)
        ast_error_continue(c->opt->check.errors, ffi_decl->decl, "first "
          "declaration is here");

      return NULL;
    }

    f_params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
    LLVMGetParamTypes(f_type, f_params);
  }

  ast_t* arg = ast_child(args);

  for(int i = 0; i < count; i++)
  {
    f_args[i] = gen_expr(c, arg);

    if(!vararg)
      f_args[i] = cast_ffi_arg(c, ffi_decl, ast, f_args[i], f_params[i],
        "parameters");

    if(f_args[i] == NULL)
    {
      ponyint_pool_free_size(buf_size, f_args);
      return NULL;
    }

    arg = ast_sibling(arg);
  }

  // If we can error out and we have an invoke target, generate an invoke
  // instead of a call.
  LLVMValueRef result;
  codegen_debugloc(c, ast);

  if(err && (c->frame->invoke_target != NULL))
    result = invoke_fun(c, func, f_args, count, "", false);
  else
    result = LLVMBuildCall(c->builder, func, f_args, count, "");

  codegen_debugloc(c, NULL);
  ponyint_pool_free_size(buf_size, f_args);

  if(!vararg)
    ponyint_pool_free_size(buf_size, f_params);

  compile_type_t* c_t = (compile_type_t*)t->c_type;

  // Special case a None return value, which is used for void functions.
  bool isnone = is_none(t->ast);
  bool isvoid = LLVMGetReturnType(f_type) == c->void_type;

  if(isnone && isvoid)
  {
    result = c_t->instance;
  } else if(isnone != isvoid) {
    report_ffi_type_err(c, ffi_decl, ast, "return values");
    return NULL;
  }

  result = cast_ffi_arg(c, ffi_decl, ast, result, c_t->use_type,
    "return values");
  result = gen_assign_cast(c, c_t->use_type, result, t->ast_cap);

  return result;
}