Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
jemc committed Jul 7, 2022
1 parent b17cee8 commit 49abe29
Show file tree
Hide file tree
Showing 8 changed files with 796 additions and 344 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ test-full-programs-release: all

test-full-programs-debug: all
@mkdir -p $(outDir)/runner-tests/debug
$(SILENT)cd '$(outDir)' && $(buildDir)/test/libponyc-run/runner/runner --timeout_s=60 --max_parallel=$(num_cores) --exclude=runner --ponyc=$(outDir)/ponyc --debug --output=$(outDir)/runner-tests/debug --test_lib=$(outDir)/test_lib $(srcDir)/test/libponyc-run
$(SILENT)cd '$(outDir)' && $(buildDir)/test/libponyc-run/runner/runner --timeout_s=60 --max_parallel=$(num_cores) --exclude=runner --ponyc=$(outDir)/ponyc --debug --output=$(outDir)/runner-tests/debug --test_lib=$(outDir)/test_lib $(srcDir)/test/libponyc-run --only ffi-tuple-parameters

test-stdlib-release: all
$(SILENT)cd '$(outDir)' && PONYPATH=.:$(PONYPATH) ./ponyc -b stdlib-release --pic --checktree --verify $(cross_args) ../../packages/stdlib && echo Built `pwd`/stdlib-release && $(cross_runner) ./stdlib-release --sequential
Expand Down
1 change: 1 addition & 0 deletions src/libponyc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ add_library(libponyc STATIC
codegen/gendesc.c
codegen/genexe.c
codegen/genexpr.c
codegen/genffi.c
codegen/genfun.c
codegen/genheader.c
codegen/genident.c
Expand Down
334 changes: 0 additions & 334 deletions src/libponyc/codegen/gencall.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,33 +58,6 @@ static size_t tuple_indices_pop(call_tuple_indices_t* ti)
return ti->data[--ti->count];
}

struct ffi_decl_t
{
LLVMValueRef func;
// First declaration encountered
ast_t* decl;
// First call encountered
ast_t* call;
};

static size_t ffi_decl_hash(ffi_decl_t* d)
{
return ponyint_hash_ptr(d->func);
}

static bool ffi_decl_cmp(ffi_decl_t* a, ffi_decl_t* b)
{
return a->func == b->func;
}

static void ffi_decl_free(ffi_decl_t* d)
{
POOL_FREE(ffi_decl_t, d);
}

DEFINE_HASHMAP(ffi_decls, ffi_decls_t, ffi_decl_t, ffi_decl_hash, ffi_decl_cmp,
ffi_decl_free);

static LLVMValueRef invoke_fun(compile_t* c, LLVMValueRef fun,
LLVMValueRef* args, int count, const char* ret, bool setcc)
{
Expand Down Expand Up @@ -1012,313 +985,6 @@ LLVMValueRef gen_pattern_eq(compile_t* c, ast_t* pattern, LLVMValueRef r_value)
return result;
}

static LLVMTypeRef ffi_return_type(compile_t* c, reach_type_t* t,
bool intrinsic)
{
compile_type_t* c_t = (compile_type_t*)t->c_type;

if(t->underlying == TK_TUPLETYPE)
{

// Can't use the named type. Build an unnamed type with the same elements.
unsigned int count = LLVMCountStructElementTypes(c_t->use_type);
size_t buf_size = count * sizeof(LLVMTypeRef);
LLVMTypeRef* e_types = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
LLVMGetStructElementTypes(c_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.
if(is_bool(child))
e_types[i] = c->i1;

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

LLVMTypeRef r_type = LLVMStructTypeInContext(c->context, e_types, count,
false);
ponyint_pool_free_size(buf_size, e_types);
return r_type;
} else if(is_none(t->ast_cap)) {
return c->void_type;
} else {
return c_t->use_type;
}
}

static LLVMValueRef declare_ffi(compile_t* c, const char* f_name,
reach_type_t* t, ast_t* args, bool intrinsic)
{
bool is_varargs = false;
ast_t* last_arg = ast_childlast(args);
int param_count = (int)ast_childcount(args);

if((last_arg != NULL) && (ast_id(last_arg) == TK_ELLIPSIS))
{
is_varargs = true;
param_count--;
}

size_t buf_size = 0;
LLVMTypeRef* f_params = NULL;
// Don't allocate argument list if all arguments are optional
if(param_count != 0)
{
buf_size = param_count * sizeof(LLVMTypeRef);
f_params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
param_count = 0;

ast_t* arg = ast_child(args);

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

while((arg != NULL) && (ast_id(arg) != TK_ELLIPSIS))
{
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[param_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, param_count, is_varargs);
LLVMValueRef func = LLVMAddFunction(c->module, f_name, f_type);

if(f_params != NULL)
ponyint_pool_free_size(buf_size, f_params);

return func;
}

static void report_ffi_type_err(compile_t* c, ffi_decl_t* decl, ast_t* ast,
const char* name)
{
ast_error(c->opt->check.errors, ast,
"conflicting calls for FFI function: %s have incompatible types",
name);

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

static LLVMValueRef cast_ffi_arg(compile_t* c, ffi_decl_t* decl, ast_t* ast,
LLVMValueRef arg, LLVMTypeRef param, const char* name)
{
if(arg == NULL)
return NULL;

LLVMTypeRef arg_type = LLVMTypeOf(arg);

if(param == arg_type)
return arg;

if((LLVMABISizeOfType(c->target_data, param) !=
LLVMABISizeOfType(c->target_data, arg_type)))
{
report_ffi_type_err(c, decl, ast, name);
return NULL;
}

switch(LLVMGetTypeKind(param))
{
case LLVMPointerTypeKind:
if(LLVMGetTypeKind(arg_type) == LLVMIntegerTypeKind)
return LLVMBuildIntToPtr(c->builder, arg, param, "");
else
return LLVMBuildBitCast(c->builder, arg, param, "");

case LLVMIntegerTypeKind:
if(LLVMGetTypeKind(arg_type) == LLVMPointerTypeKind)
return LLVMBuildPtrToInt(c->builder, arg, param, "");

break;

case LLVMStructTypeKind:
pony_assert(LLVMGetTypeKind(arg_type) == LLVMStructTypeKind);
return arg;

default: {}
}

pony_assert(false);
return NULL;
}

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)
{
// Prototypes are mandatory, the declaration is already stored.
ast_t* decl = (ast_t*)ast_data(ast);
pony_assert(decl != NULL);

bool is_intrinsic = (!strncmp(f_name, "llvm.", 5) || !strncmp(f_name, "internal.", 9));
AST_GET_CHILDREN(decl, decl_id, decl_ret, decl_params, decl_named_params, decl_err);
err = (ast_id(decl_err) == TK_QUESTION);
func = declare_ffi(c, f_name, t, decl_params, is_intrinsic);

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;
ffi_decl->call = 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_P(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;
}

LLVMValueRef gencall_runtime(compile_t* c, const char *name,
LLVMValueRef* args, int count, const char* ret)
{
Expand Down
Loading

0 comments on commit 49abe29

Please sign in to comment.