// TODO: Make group signature indiependent of package load order. const char* package_group_signature(package_group_t* group) { if(group->signature == NULL) { pony_ctx_t ctx; memset(&ctx, 0, sizeof(pony_ctx_t)); ponyint_array_t array; memset(&array, 0, sizeof(ponyint_array_t)); char* buf = (char*)ponyint_pool_alloc_size(SIGNATURE_LENGTH); pony_serialise(&ctx, group, package_group_signature_pony_type(), &array, s_alloc_fn, s_throw_fn); int status = blake2b(buf, SIGNATURE_LENGTH, array.ptr, array.size, NULL, 0); (void)status; pony_assert(status == 0); group->signature = buf; ponyint_pool_free_size(array.size, array.ptr); } return group->signature; }
static trace_t trace_type(ast_t* type) { switch(ast_id(type)) { case TK_UNIONTYPE: return trace_type_union(type); case TK_ISECTTYPE: return trace_type_isect(type); case TK_TUPLETYPE: return TRACE_TUPLE; case TK_NOMINAL: return trace_type_nominal(type); default: {} } pony_assert(0); return TRACE_DYNAMIC; }
void printbuf(printbuf_t* buf, const char* fmt, ...) { size_t avail = buf->size - buf->offset; va_list ap; va_start(ap, fmt); int r = vsnprintf(buf->m + buf->offset, avail, fmt, ap); va_end(ap); if(r < 0) { #ifdef PLATFORM_IS_WINDOWS va_start(ap, fmt); r = _vscprintf(fmt, ap); va_end(ap); if(r < 0) return; #else return; #endif } if((size_t)r >= avail) { size_t new_size = buf->size + r + 1; buf->m = (char*)ponyint_pool_realloc_size(buf->size, new_size, buf->m); buf->size = new_size; avail = buf->size - buf->offset; va_start(ap, fmt); r = vsnprintf(buf->m + buf->offset, avail, fmt, ap); va_end(ap); pony_assert((r > 0) && ((size_t)r < buf->size)); } buf->offset += r; }
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; }
// Flatten a provides type into a list, checking all types are traits or // interfaces static ast_result_t flatten_provides_list(pass_opt_t* opt, ast_t* provider, int index) { pony_assert(provider != NULL); ast_t* provides = ast_childidx(provider, index); if(ast_id(provides) == TK_NONE) return AST_OK; ast_t* list = ast_from(provides, TK_PROVIDES); ast_t* list_end = NULL; if(!flatten_provided_type(opt, provides, provider, list, &list_end)) { ast_free(list); return AST_ERROR; } ast_replace(&provides, list); return AST_OK; }
static const char* quoted_locator(pass_opt_t* opt, ast_t* use, const char* locator) { pony_assert(locator != NULL); if(strpbrk(locator, INVALID_LOCATOR_CHARS) != NULL) { if(use != NULL) ast_error(opt->check.errors, use, "use URI contains invalid characters"); return NULL; } size_t len = strlen(locator); char* quoted = (char*)ponyint_pool_alloc_size(len + 3); quoted[0] = '"'; memcpy(quoted + 1, locator, len); quoted[len + 1] = '"'; quoted[len + 2] = '\0'; return stringtab_consume(quoted, len + 3); }
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) { pony_assert(intrinsic); // 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); 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; } }
bool method_check_type_params(pass_opt_t* opt, ast_t** astp) { ast_t* lhs = *astp; ast_t* type = ast_type(lhs); if(is_typecheck_error(type)) return false; ast_t* typeparams = ast_childidx(type, 1); pony_assert(ast_id(type) == TK_FUNTYPE); if(ast_id(typeparams) == TK_NONE) return true; BUILD(typeargs, ast_parent(lhs), NODE(TK_TYPEARGS)); if(!reify_defaults(typeparams, typeargs, true, opt)) { ast_free_unattached(typeargs); return false; } if(!check_constraints(lhs, typeparams, typeargs, true, opt)) { ast_free_unattached(typeargs); return false; } type = reify(type, typeparams, typeargs, opt, true); typeparams = ast_childidx(type, 1); ast_replace(&typeparams, ast_from(typeparams, TK_NONE)); REPLACE(astp, NODE(ast_id(lhs), TREE(lhs) TREE(typeargs))); ast_settype(*astp, type); return true; }
static trace_t trace_union_tag(trace_t a) { switch(a) { case TRACE_MACHINE_WORD: case TRACE_PRIMITIVE: case TRACE_TAG_KNOWN: case TRACE_TAG_UNKNOWN: return TRACE_TAG_UNKNOWN; case TRACE_VAL_KNOWN: case TRACE_VAL_UNKNOWN: case TRACE_MUT_KNOWN: case TRACE_MUT_UNKNOWN: case TRACE_DYNAMIC: case TRACE_TUPLE: return TRACE_DYNAMIC; default: {} } pony_assert(0); return TRACE_NONE; }
static bool catch_up_provides(pass_opt_t* opt, ast_t* provides) { // Make sure all traits have been through the expr pass before proceeding. // We run into dangling ast_data pointer problems when we inherit methods from // traits that have only been through the refer pass, and not the expr pass. ast_t* child = ast_child(provides); while(child != NULL) { if(!ast_passes_subtree(&child, opt, PASS_EXPR)) return false; ast_t* def = (ast_t*)ast_data(child); pony_assert(def != NULL); if(!ast_passes_type(&def, opt, PASS_EXPR)) return false; child = ast_sibling(child); } return true; }
const char* program_signature(ast_t* program) { pony_assert(program != NULL); pony_assert(ast_id(program) == TK_PROGRAM); program_t* data = (program_t*)ast_data(program); pony_assert(data != NULL); if(data->signature == NULL) { ast_t* first_package = ast_child(program); pony_assert(first_package != NULL); pony_assert(data->package_groups == NULL); data->package_groups = package_dependency_groups(first_package); blake2b_state hash_state; int status = blake2b_init(&hash_state, SIGNATURE_LENGTH); pony_assert(status == 0); package_group_list_t* iter = data->package_groups; while(iter != NULL) { package_group_t* group = package_group_list_data(iter); const char* group_sig = package_group_signature(group); blake2b_update(&hash_state, group_sig, SIGNATURE_LENGTH); iter = package_group_list_next(iter); } data->signature = (char*)ponyint_pool_alloc_size(SIGNATURE_LENGTH); status = blake2b_final(&hash_state, data->signature, SIGNATURE_LENGTH); pony_assert(status == 0); } return data->signature; }
// Coerce a literal control block to be the specified target type static bool coerce_control_block(ast_t** astp, ast_t* target_type, lit_chain_t* chain, pass_opt_t* opt, bool report_errors) { pony_assert(astp != NULL); ast_t* literal_expr = *astp; pony_assert(literal_expr != NULL); ast_t* lit_type = ast_type(literal_expr); pony_assert(lit_type != NULL); pony_assert(ast_id(lit_type) == TK_LITERAL); ast_t* block_type = ast_type(lit_type); for(ast_t* p = ast_child(lit_type); p != NULL; p = ast_sibling(p)) { pony_assert(ast_id(p) == TK_LITERALBRANCH); ast_t* branch = (ast_t*)ast_data(p); pony_assert(branch != NULL); if(!coerce_literal_to_type(&branch, target_type, chain, opt, report_errors)) { ast_free_unattached(block_type); return false; } block_type = type_union(opt, block_type, ast_type(branch)); } if(is_typecheck_error(block_type)) return false; // block_type may be a sub-tree of the current type of literal_expr. // This means we must copy it before setting it as the type since ast_settype // will first free the existing type of literal_expr, which may include // block_type. if(ast_parent(block_type) != NULL) block_type = ast_dup(block_type); ast_settype(literal_expr, block_type); return true; }
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; }
void program_lib_build_args(ast_t* program, pass_opt_t* opt, const char* path_preamble, const char* rpath_preamble, const char* global_preamble, const char* global_postamble, const char* lib_premable, const char* lib_postamble) { pony_assert(program != NULL); pony_assert(ast_id(program) == TK_PROGRAM); pony_assert(global_preamble != NULL); pony_assert(global_postamble != NULL); pony_assert(lib_premable != NULL); pony_assert(lib_postamble != NULL); program_t* data = (program_t*)ast_data(program); pony_assert(data != NULL); pony_assert(data->lib_args == NULL); // Not yet built args // Start with an arbitrary amount of space data->lib_args_alloced = 256; data->lib_args = (char*)ponyint_pool_alloc_size(data->lib_args_alloced); data->lib_args[0] = '\0'; data->lib_args_size = 0; // Library paths defined in the source code. for(strlist_t* p = data->libpaths; p != NULL; p = strlist_next(p)) { const char* libpath = strlist_data(p); append_to_args(data, path_preamble); append_to_args(data, libpath); append_to_args(data, " "); if(rpath_preamble != NULL) { append_to_args(data, rpath_preamble); append_to_args(data, libpath); append_to_args(data, " "); } } // Library paths from the command line and environment variable. for(strlist_t* p = opt->package_search_paths; p != NULL; p = strlist_next(p)) { const char* libpath = quoted_locator(opt, NULL, strlist_data(p)); if(libpath == NULL) continue; append_to_args(data, path_preamble); append_to_args(data, libpath); append_to_args(data, " "); if(rpath_preamble != NULL) { append_to_args(data, rpath_preamble); append_to_args(data, libpath); append_to_args(data, " "); } } // Library names. append_to_args(data, global_preamble); for(strlist_t* p = data->libs; p != NULL; p = strlist_next(p)) { const char* lib = strlist_data(p); bool amble = !is_path_absolute(&lib[1]); if(amble) append_to_args(data, lib_premable); append_to_args(data, lib); if(amble) append_to_args(data, lib_postamble); append_to_args(data, " "); } append_to_args(data, global_postamble); }
// Sugar for partial application, which we convert to a lambda. static bool partial_application(pass_opt_t* opt, ast_t** astp) { /* Example that we refer to throughout this function. * ```pony * class C * fun f[T](a: A, b: B = b_default): R * * let recv: T = ... * recv~f[T2](foo) * ``` * * Partial call is converted to: * ```pony * {(b: B = b_default)($0 = recv, a = foo): R => $0.f[T2](a, consume b) } * ``` */ ast_t* ast = *astp; typecheck_t* t = &opt->check; if(!method_application(opt, ast, true)) return false; AST_GET_CHILDREN(ast, positional, namedargs, question, lhs); // LHS must be an application, possibly wrapped in another application // if the method had type parameters for qualification. pony_assert(ast_id(lhs) == TK_FUNAPP || ast_id(lhs) == TK_BEAPP || ast_id(lhs) == TK_NEWAPP); AST_GET_CHILDREN(lhs, receiver, method); ast_t* type_args = NULL; if(ast_id(receiver) == ast_id(lhs)) { type_args = method; AST_GET_CHILDREN_NO_DECL(receiver, receiver, method); } // Look up the original method definition for this method call. ast_t* method_def = lookup(opt, lhs, ast_type(receiver), ast_name(method)); pony_assert(ast_id(method_def) == TK_FUN || ast_id(method_def) == TK_BE || ast_id(method_def) == TK_NEW); // The TK_FUNTYPE of the LHS. ast_t* type = ast_type(lhs); pony_assert(ast_id(type) == TK_FUNTYPE); if(is_typecheck_error(type)) return false; AST_GET_CHILDREN(type, cap, type_params, target_params, result); bool bare = ast_id(cap) == TK_AT; token_id apply_cap = TK_AT; if(!bare) apply_cap = partial_application_cap(opt, type, receiver, positional); token_id can_error = ast_id(ast_childidx(method_def, 5)); const char* recv_name = package_hygienic_id(t); // Build lambda expression. ast_t* call_receiver = NULL; if(bare) { ast_t* arg = ast_child(positional); while(arg != NULL) { if(ast_id(arg) != TK_NONE) { ast_error(opt->check.errors, arg, "the partial application of a bare " "method cannot take arguments"); return false; } arg = ast_sibling(arg); } ast_t* receiver_type = ast_type(receiver); if(is_bare(receiver_type)) { // Partial application on a bare object, simply return the object itself. ast_replace(astp, receiver); return true; } AST_GET_CHILDREN(receiver_type, recv_type_package, recv_type_name); const char* recv_package_str = ast_name(recv_type_package); const char* recv_name_str = ast_name(recv_type_name); ast_t* module = ast_nearest(ast, TK_MODULE); ast_t* package = ast_parent(module); ast_t* pkg_id = package_id(package); const char* pkg_str = ast_name(pkg_id); const char* pkg_alias = NULL; if(recv_package_str != pkg_str) pkg_alias = package_alias_from_id(module, recv_package_str); ast_free_unattached(pkg_id); if(pkg_alias != NULL) { // `package.Type.f` BUILD_NO_DECL(call_receiver, ast, NODE(TK_DOT, NODE(TK_DOT, NODE(TK_REFERENCE, ID(pkg_alias)) ID(recv_name_str)) TREE(method))); } else { // `Type.f` BUILD_NO_DECL(call_receiver, ast, NODE(TK_DOT, NODE(TK_REFERENCE, ID(recv_name_str)) TREE(method))); } } else { // `$0.f` BUILD_NO_DECL(call_receiver, ast, NODE(TK_DOT, NODE(TK_REFERENCE, ID(recv_name)) TREE(method))); } ast_t* captures = NULL; if(bare) { captures = ast_from(receiver, TK_NONE); } else { // Build captures. We always have at least one capture, for receiver. // Capture: `$0 = recv` BUILD_NO_DECL(captures, receiver, NODE(TK_LAMBDACAPTURES, NODE(TK_LAMBDACAPTURE, ID(recv_name) NONE // Infer type. TREE(receiver)))); } // Process arguments. ast_t* target_param = ast_child(target_params); ast_t* lambda_params = ast_from(target_params, TK_NONE); ast_t* lambda_call_args = ast_from(positional, TK_NONE); ast_t* given_arg = ast_child(positional); while(given_arg != NULL) { pony_assert(target_param != NULL); const char* target_p_name = ast_name(ast_child(target_param)); if(ast_id(given_arg) == TK_NONE) { // This argument is not supplied already, must be a lambda parameter. // Like `b` in example above. // Build a new a new TK_PARAM node rather than copying the target one, // since the target has already been processed to expr pass, and we need // a clean one. AST_GET_CHILDREN(target_param, p_id, p_type, p_default); // Parameter: `b: B = b_default` BUILD(lambda_param, target_param, NODE(TK_PARAM, TREE(p_id) TREE(sanitise_type(p_type)) TREE(p_default))); ast_append(lambda_params, lambda_param); ast_setid(lambda_params, TK_PARAMS); // Argument: `consume b` BUILD(target_arg, lambda_param, NODE(TK_SEQ, NODE(TK_CONSUME, NONE NODE(TK_REFERENCE, ID(target_p_name))))); ast_append(lambda_call_args, target_arg); ast_setid(lambda_call_args, TK_POSITIONALARGS); } else { // This argument is supplied to the partial, capture it. // Like `a` in example above. // Capture: `a = foo` BUILD(capture, given_arg, NODE(TK_LAMBDACAPTURE, ID(target_p_name) NONE TREE(given_arg))); ast_append(captures, capture); // Argument: `a` BUILD(target_arg, given_arg, NODE(TK_SEQ, NODE(TK_REFERENCE, ID(target_p_name)))); ast_append(lambda_call_args, target_arg); ast_setid(lambda_call_args, TK_POSITIONALARGS); } given_arg = ast_sibling(given_arg); target_param = ast_sibling(target_param); } pony_assert(target_param == NULL); if(type_args != NULL) { // The partial call has type args, add them to the actual call in apply(). // `$0.f[T2]` BUILD(qualified, type_args, NODE(TK_QUALIFY, TREE(call_receiver) TREE(type_args))); call_receiver = qualified; } REPLACE(astp, NODE((bare ? TK_BARELAMBDA : TK_LAMBDA), NODE(apply_cap) NONE // Lambda function name. NONE // Lambda type params. TREE(lambda_params) TREE(captures) TREE(sanitise_type(result)) NODE(can_error) NODE(TK_SEQ, NODE(TK_CALL, TREE(lambda_call_args) NONE // Named args. NODE(can_error) TREE(call_receiver))) NONE)); // Lambda reference capability. // Need to preserve various lambda children. ast_setflag(ast_childidx(*astp, 2), AST_FLAG_PRESERVE); // Type params. ast_setflag(ast_childidx(*astp, 3), AST_FLAG_PRESERVE); // Parameters. ast_setflag(ast_childidx(*astp, 5), AST_FLAG_PRESERVE); // Return type. ast_setflag(ast_childidx(*astp, 7), AST_FLAG_PRESERVE); // Body. // Catch up to this pass. return ast_passes_subtree(astp, opt, PASS_EXPR); }
// Normalise the given ifdef condition. static void cond_normalise(ast_t** astp) { pony_assert(astp != NULL); ast_t* ast = *astp; pony_assert(ast != NULL); switch(ast_id(ast)) { case TK_AND: { ast_setid(ast, TK_IFDEFAND); AST_GET_CHILDREN(ast, left, right, question); pony_assert(ast_id(question) == TK_NONE); ast_remove(question); cond_normalise(&left); cond_normalise(&right); break; } case TK_OR: { ast_setid(ast, TK_IFDEFOR); AST_GET_CHILDREN(ast, left, right, question); pony_assert(ast_id(question) == TK_NONE); ast_remove(question); cond_normalise(&left); cond_normalise(&right); break; } case TK_NOT: { ast_setid(ast, TK_IFDEFNOT); AST_GET_CHILDREN(ast, child); cond_normalise(&child); break; } case TK_STRING: { ast_setid(ast, TK_ID); REPLACE(astp, NODE(TK_IFDEFFLAG, TREE(*astp))); break; } case TK_REFERENCE: { const char* name = ast_name(ast_child(ast)); if(strcmp(name, OS_POSIX_NAME) == 0) { REPLACE(astp, NODE(TK_IFDEFOR, NODE(TK_IFDEFOR, NODE(TK_IFDEFFLAG, ID(OS_LINUX_NAME)) NODE(TK_IFDEFFLAG, ID(OS_MACOSX_NAME))) NODE(TK_IFDEFFLAG, ID(OS_BSD_NAME)))); break; } ast_setid(ast, TK_IFDEFFLAG); break; } case TK_SEQ: { // Remove the sequence node. pony_assert(ast_childcount(ast) == 1); ast_t* child = ast_pop(ast); pony_assert(child != NULL); cond_normalise(&child); ast_replace(astp, child); break; } case TK_IFDEFAND: case TK_IFDEFOR: case TK_IFDEFNOT: case TK_IFDEFFLAG: // Already normalised. break; default: pony_assert(0); break; } }
// Find the declaration for the specified FFI name that is valid for the given // build config. // The declaration found is stored in the given decl info argument. // There must be exactly one valid declaration. // If a declaration is already specified in the given decl info this must be // the same as the one found. // All other cases are errors, which will be reported by this function. // Returns: true on success, false on failure. static bool find_decl_for_config(ast_t* call, ast_t* package, const char* ffi_name, buildflagset_t* config, ffi_decl_t* decl_info, pass_opt_t* opt) { pony_assert(call != NULL); pony_assert(package != NULL); pony_assert(ffi_name != NULL); pony_assert(config != NULL); pony_assert(decl_info != NULL); bool had_valid_decl = false; // FFI declarations are package wide, so check all modules in package. for(ast_t* m = ast_child(package); m != NULL; m = ast_sibling(m)) { // Find all the FFI declarations in this module. for(ast_t* use = ast_child(m); use != NULL; use = ast_sibling(use)) { if(ast_id(use) == TK_USE) { AST_GET_CHILDREN(use, alias, decl, guard); if(ast_id(decl) == TK_FFIDECL && ffi_name == ast_name(ast_child(decl))) { // We have an FFI declaration for the specified name. if(cond_eval(guard, config, false, opt)) { // This declaration is valid for this config. had_valid_decl = true; if(decl_info->decl != NULL) { // We already have a decalaration, is it the same one? if(decl_info->decl != decl) { ast_error(opt->check.errors, call, "Multiple possible declarations for FFI call"); ast_error_continue(opt->check.errors, decl_info->decl, "This declaration valid for config: %s", decl_info->config); ast_error_continue(opt->check.errors, decl, "This declaration valid for config: %s", buildflagset_print(config)); return false; } } else { // Store the declaration found. // We store the config string incase we need it for error // messages later. We stringtab it because the version we are // given is in a temporary buffer. decl_info->decl = decl; decl_info->config = stringtab(buildflagset_print(config)); } } } } } } if(!had_valid_decl) { ast_error(opt->check.errors, call, "No FFI declaration found for '%s' in config: %s", ffi_name, buildflagset_print(config)); return false; } return true; }
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; }
static void release_local_actor(gc_t* gc) { pony_assert(gc->rc > 0); gc->rc--; }
LLVMValueRef gen_pattern_eq(compile_t* c, ast_t* pattern, LLVMValueRef r_value) { // This is used for structural equality in pattern matching. ast_t* pattern_type = ast_type(pattern); if(ast_id(pattern_type) == TK_NOMINAL) { AST_GET_CHILDREN(pattern_type, package, id); // Special case equality on primitive types. if(ast_name(package) == c->str_builtin) { const char* name = ast_name(id); if((name == c->str_Bool) || (name == c->str_I8) || (name == c->str_I16) || (name == c->str_I32) || (name == c->str_I64) || (name == c->str_I128) || (name == c->str_ILong) || (name == c->str_ISize) || (name == c->str_U8) || (name == c->str_U16) || (name == c->str_U32) || (name == c->str_U64) || (name == c->str_U128) || (name == c->str_ULong) || (name == c->str_USize) || (name == c->str_F32) || (name == c->str_F64) ) { return gen_eq_rvalue(c, pattern, r_value, true); } } } // Generate the receiver. LLVMValueRef l_value = gen_expr(c, pattern); reach_type_t* t = reach_type(c->reach, pattern_type); pony_assert(t != NULL); // Static or virtual dispatch. token_id cap = cap_dispatch(pattern_type); reach_method_t* m = reach_method(t, cap, c->str_eq, NULL); LLVMValueRef func = dispatch_function(c, t, m, l_value); if(func == NULL) return NULL; // Call the function. We know it isn't partial. LLVMValueRef args[2]; args[0] = l_value; args[1] = r_value; codegen_debugloc(c, pattern); LLVMValueRef result = codegen_call(c, func, args, 2); codegen_debugloc(c, NULL); return result; }
// Report whether the named platform attribute is true bool os_is_target(const char* attribute, bool release, bool* out_is_target, pass_opt_t* options) { pony_assert(attribute != NULL); pony_assert(out_is_target != NULL); pony_assert(options != NULL); if(!strcmp(attribute, OS_BSD_NAME)) { *out_is_target = target_is_bsd(options->triple); return true; } if(!strcmp(attribute, OS_FREEBSD_NAME)) { *out_is_target = target_is_freebsd(options->triple); return true; } if(!strcmp(attribute, OS_DRAGONFLY_NAME)) { *out_is_target = target_is_dragonfly(options->triple); return true; } if(!strcmp(attribute, OS_LINUX_NAME)) { *out_is_target = target_is_linux(options->triple); return true; } if(!strcmp(attribute, OS_MACOSX_NAME)) { *out_is_target = target_is_macosx(options->triple); return true; } if(!strcmp(attribute, OS_WINDOWS_NAME)) { *out_is_target = target_is_windows(options->triple); return true; } if(!strcmp(attribute, OS_POSIX_NAME)) { *out_is_target = target_is_posix(options->triple); return true; } if(!strcmp(attribute, OS_X86_NAME)) { *out_is_target = target_is_x86(options->triple); return true; } if(!strcmp(attribute, OS_ARM_NAME)) { *out_is_target = target_is_arm(options->triple); return true; } if(!strcmp(attribute, OS_LP64_NAME)) { *out_is_target = target_is_lp64(options->triple); return true; } if(!strcmp(attribute, OS_LLP64_NAME)) { *out_is_target = target_is_llp64(options->triple); return true; } if(!strcmp(attribute, OS_ILP32_NAME)) { *out_is_target = target_is_ilp32(options->triple); return true; } if(!strcmp(attribute, OS_NATIVE128_NAME)) { *out_is_target = target_is_native128(options->triple); return true; } if(!strcmp(attribute, OS_DEBUG_NAME)) { *out_is_target = !release; return true; } return false; }
void errorframev(errorframe_t* frame, source_t* source, size_t line, size_t pos, const char* fmt, va_list ap) { pony_assert(frame != NULL); append_to_frame(frame, make_errorv(source, line, pos, fmt, ap)); }
bool check_constraints(ast_t* orig, ast_t* typeparams, ast_t* typeargs, bool report_errors, pass_opt_t* opt) { ast_t* typeparam = ast_child(typeparams); ast_t* typearg = ast_child(typeargs); while(typeparam != NULL) { if(is_bare(typearg)) { if(report_errors) { ast_error(opt->check.errors, typearg, "a bare type cannot be used as a type argument"); } return false; } switch(ast_id(typearg)) { case TK_NOMINAL: { ast_t* def = (ast_t*)ast_data(typearg); if(ast_id(def) == TK_STRUCT) { if(report_errors) { ast_error(opt->check.errors, typearg, "a struct cannot be used as a type argument"); } return false; } break; } case TK_TYPEPARAMREF: { ast_t* def = (ast_t*)ast_data(typearg); if(def == typeparam) { typeparam = ast_sibling(typeparam); typearg = ast_sibling(typearg); continue; } break; } default: {} } // Reify the constraint. ast_t* constraint = ast_childidx(typeparam, 1); ast_t* r_constraint = reify(constraint, typeparams, typeargs, opt, true); // A bound type must be a subtype of the constraint. errorframe_t info = NULL; errorframe_t* infop = (report_errors ? &info : NULL); if(!is_subtype_constraint(typearg, r_constraint, infop, opt)) { if(report_errors) { errorframe_t frame = NULL; ast_error_frame(&frame, orig, "type argument is outside its constraint"); ast_error_frame(&frame, typearg, "argument: %s", ast_print_type(typearg)); ast_error_frame(&frame, typeparam, "constraint: %s", ast_print_type(r_constraint)); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); } ast_free_unattached(r_constraint); return false; } ast_free_unattached(r_constraint); // A constructable constraint can only be fulfilled by a concrete typearg. if(is_constructable(constraint) && !is_concrete(typearg)) { if(report_errors) { ast_error(opt->check.errors, orig, "a constructable constraint can " "only be fulfilled by a concrete type argument"); ast_error_continue(opt->check.errors, typearg, "argument: %s", ast_print_type(typearg)); ast_error_continue(opt->check.errors, typeparam, "constraint: %s", ast_print_type(constraint)); } return false; } typeparam = ast_sibling(typeparam); typearg = ast_sibling(typearg); } pony_assert(typeparam == NULL); pony_assert(typearg == NULL); return true; }
PONY_API void pony_asio_event_subscribe(asio_event_t* ev) { if((ev == NULL) || (ev->flags == ASIO_DISPOSABLE) || (ev->flags == ASIO_DESTROYED)) { pony_assert(0); return; } asio_backend_t* b = ponyint_asio_get_backend(); pony_assert(b != NULL); if(ev->noisy) { uint64_t old_count = ponyint_asio_noisy_add(); // tell scheduler threads that asio has at least one noisy actor // if the old_count was 0 if (old_count == 0) ponyint_sched_noisy_asio(SPECIAL_THREADID_EPOLL); } struct epoll_event ep; ep.data.ptr = ev; ep.events = EPOLLRDHUP; if(ev->flags & ASIO_READ) ep.events |= EPOLLIN; if(ev->flags & ASIO_WRITE) ep.events |= EPOLLOUT; if(ev->flags & ASIO_TIMER) { ev->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); timer_set_nsec(ev->fd, ev->nsec); ep.events |= EPOLLIN; } if(ev->flags & ASIO_SIGNAL) { int sig = (int)ev->nsec; asio_event_t* prev = NULL; #ifdef USE_VALGRIND ANNOTATE_HAPPENS_BEFORE(&b->sighandlers[sig]); #endif if((sig < MAX_SIGNAL) && atomic_compare_exchange_strong_explicit(&b->sighandlers[sig], &prev, ev, memory_order_release, memory_order_relaxed)) { struct sigaction new_action; new_action.sa_handler = signal_handler; sigemptyset (&new_action.sa_mask); // ask to restart interrupted syscalls to match `signal` behavior new_action.sa_flags = SA_RESTART; sigaction(sig, &new_action, NULL); ev->fd = eventfd(0, EFD_NONBLOCK); ep.events |= EPOLLIN; } else { return; } } if(ev->flags & ASIO_ONESHOT) { ep.events |= EPOLLONESHOT; } else { // Only use edge triggering if one shot isn't enabled. // This is because of how the runtime gets notifications // from epoll in this ASIO thread and then notifies the // appropriate actor to read/write as necessary. // specifically, it seems there's an edge case/race condition // with edge triggering where if there is already data waiting // on the socket, then epoll might not be triggering immediately // when an edge triggered epoll request is made. ep.events |= EPOLLET; } epoll_ctl(b->epfd, EPOLL_CTL_ADD, ev->fd, &ep); }
PONY_API void pony_asio_event_unsubscribe(asio_event_t* ev) { if((ev == NULL) || (ev->flags == ASIO_DISPOSABLE) || (ev->flags == ASIO_DESTROYED)) { pony_assert(0); return; } asio_backend_t* b = ponyint_asio_get_backend(); pony_assert(b != NULL); if(ev->noisy) { uint64_t old_count = ponyint_asio_noisy_remove(); // tell scheduler threads that asio has no noisy actors // if the old_count was 1 if (old_count == 1) { ponyint_sched_unnoisy_asio(SPECIAL_THREADID_EPOLL); // maybe wake up a scheduler thread if they've all fallen asleep ponyint_sched_maybe_wakeup_if_all_asleep(-1); } ev->noisy = false; } epoll_ctl(b->epfd, EPOLL_CTL_DEL, ev->fd, NULL); if(ev->flags & ASIO_TIMER) { if(ev->fd != -1) { close(ev->fd); ev->fd = -1; } } if(ev->flags & ASIO_SIGNAL) { int sig = (int)ev->nsec; asio_event_t* prev = ev; #ifdef USE_VALGRIND ANNOTATE_HAPPENS_BEFORE(&b->sighandlers[sig]); #endif if((sig < MAX_SIGNAL) && atomic_compare_exchange_strong_explicit(&b->sighandlers[sig], &prev, NULL, memory_order_release, memory_order_relaxed)) { struct sigaction new_action; #if !defined(USE_SCHEDULER_SCALING_PTHREADS) // Make sure we ignore signals related to scheduler sleeping/waking // as the default for those signals is termination if(sig == PONY_SCHED_SLEEP_WAKE_SIGNAL) new_action.sa_handler = empty_signal_handler; else #endif new_action.sa_handler = SIG_DFL; sigemptyset (&new_action.sa_mask); // ask to restart interrupted syscalls to match `signal` behavior new_action.sa_flags = SA_RESTART; sigaction(sig, &new_action, NULL); close(ev->fd); ev->fd = -1; } } ev->flags = ASIO_DISPOSABLE; send_request(ev, ASIO_DISPOSABLE); }
static size_t tuple_indices_pop(call_tuple_indices_t* ti) { pony_assert(ti->count > 0); return ti->data[--ti->count]; }
static LLVMValueRef gen_digestof_value(compile_t* c, ast_t* type, LLVMValueRef value) { LLVMTypeRef impl_type = LLVMTypeOf(value); switch(LLVMGetTypeKind(impl_type)) { case LLVMFloatTypeKind: value = LLVMBuildBitCast(c->builder, value, c->i32, ""); return LLVMBuildZExt(c->builder, value, c->intptr, ""); case LLVMDoubleTypeKind: value = LLVMBuildBitCast(c->builder, value, c->i64, ""); return gen_digestof_int64(c, value); case LLVMIntegerTypeKind: { uint32_t width = LLVMGetIntTypeWidth(impl_type); if(width < 64) { return LLVMBuildZExt(c->builder, value, c->intptr, ""); } else if(width == 64) { return gen_digestof_int64(c, value); } else if(width == 128) { LLVMValueRef shift = LLVMConstInt(c->i128, 64, false); LLVMValueRef high = LLVMBuildLShr(c->builder, value, shift, ""); high = LLVMBuildTrunc(c->builder, high, c->i64, ""); value = LLVMBuildTrunc(c->builder, value, c->i64, ""); high = gen_digestof_int64(c, high); value = gen_digestof_int64(c, value); return LLVMBuildXor(c->builder, value, high, ""); } break; } case LLVMStructTypeKind: { uint32_t count = LLVMCountStructElementTypes(impl_type); LLVMValueRef result = LLVMConstInt(c->intptr, 0, false); ast_t* child = ast_child(type); for(uint32_t i = 0; i < count; i++) { LLVMValueRef elem = LLVMBuildExtractValue(c->builder, value, i, ""); elem = gen_digestof_value(c, child, elem); result = LLVMBuildXor(c->builder, result, elem, ""); child = ast_sibling(child); } pony_assert(child == NULL); return result; } case LLVMPointerTypeKind: if(!is_known(type)) { reach_type_t* t = reach_type(c->reach, type); int sub_kind = subtype_kind(t); if((sub_kind & SUBTYPE_KIND_BOXED) != 0) return gen_digestof_box(c, t, value, sub_kind); } return LLVMBuildPtrToInt(c->builder, value, c->intptr, ""); default: {} } pony_assert(0); return NULL; }
static bool can_inline_message_send(reach_type_t* t, reach_method_t* m, const char* method_name) { switch(t->underlying) { case TK_CLASS: case TK_STRUCT: case TK_PRIMITIVE: return false; case TK_ACTOR: return true; default: {} } size_t i = HASHMAP_BEGIN; reach_type_t* sub; while((sub = reach_type_cache_next(&t->subtypes, &i)) != NULL) { reach_method_t* m_sub = reach_method(sub, m->cap, method_name, m->typeargs); if(m_sub == NULL) continue; switch(sub->underlying) { case TK_CLASS: case TK_PRIMITIVE: return false; case TK_ACTOR: if(ast_id(m_sub->r_fun) == TK_FUN) return false; break; default: {} } pony_assert(m->param_count == m_sub->param_count); for(size_t i = 0; i < m->param_count; i++) { // If the param is a boxable type for us and an unboxable type for one of // our subtypes, that subtype will take that param as boxed through an // interface. In order to correctly box the value the actual function to // call must be resolved through name mangling, therefore we can't inline // the message send. reach_type_t* param = m->params[i].type; reach_type_t* sub_param = m_sub->params[i].type; if(param->can_be_boxed) { if(!sub_param->can_be_boxed) return false; if(param->underlying == TK_TUPLETYPE) { ast_t* child = ast_child(param->ast); while(child != NULL) { if(contains_boxable(child)) return false; } } } } } return true; }
bool errorframe_has_errors(errorframe_t* frame) { pony_assert(frame != NULL); return frame != NULL; }
LLVMValueRef gen_call(compile_t* c, ast_t* ast) { // Special case calls. LLVMValueRef special; if(special_case_call(c, ast, &special)) return special; AST_GET_CHILDREN(ast, positional, named, postfix); AST_GET_CHILDREN(postfix, receiver, method); ast_t* typeargs = NULL; // Dig through function qualification. switch(ast_id(receiver)) { case TK_NEWREF: case TK_NEWBEREF: case TK_BEREF: case TK_FUNREF: case TK_BECHAIN: case TK_FUNCHAIN: typeargs = method; AST_GET_CHILDREN_NO_DECL(receiver, receiver, method); break; default: {} } // Get the receiver type. const char* method_name = ast_name(method); ast_t* type = ast_type(receiver); reach_type_t* t = reach_type(c->reach, type); pony_assert(t != NULL); // Generate the arguments. size_t count = ast_childcount(positional) + 1; size_t buf_size = count * sizeof(void*); LLVMValueRef* args = (LLVMValueRef*)ponyint_pool_alloc_size(buf_size); ast_t* arg = ast_child(positional); int i = 1; while(arg != NULL) { LLVMValueRef value = gen_expr(c, arg); if(value == NULL) { ponyint_pool_free_size(buf_size, args); return NULL; } args[i] = value; arg = ast_sibling(arg); i++; } bool is_new_call = false; // Generate the receiver. Must be done after the arguments because the args // could change things in the receiver expression that must be accounted for. if(call_needs_receiver(postfix, t)) { switch(ast_id(postfix)) { case TK_NEWREF: case TK_NEWBEREF: { call_tuple_indices_t tuple_indices = {NULL, 0, 4}; tuple_indices.data = (size_t*)ponyint_pool_alloc_size(4 * sizeof(size_t)); ast_t* current = ast; ast_t* parent = ast_parent(current); while((parent != NULL) && (ast_id(parent) != TK_ASSIGN) && (ast_id(parent) != TK_CALL)) { if(ast_id(parent) == TK_TUPLE) { size_t index = 0; ast_t* child = ast_child(parent); while(current != child) { ++index; child = ast_sibling(child); } tuple_indices_push(&tuple_indices, index); } current = parent; parent = ast_parent(current); } // If we're constructing an embed field, pass a pointer to the field // as the receiver. Otherwise, allocate an object. if((parent != NULL) && (ast_id(parent) == TK_ASSIGN)) { size_t index = 1; current = ast_childidx(parent, 1); while((ast_id(current) == TK_TUPLE) || (ast_id(current) == TK_SEQ)) { parent = current; if(ast_id(current) == TK_TUPLE) { // If there are no indices left, we're destructuring a tuple. // Errors in those cases have already been catched by the expr // pass. if(tuple_indices.count == 0) break; index = tuple_indices_pop(&tuple_indices); current = ast_childidx(parent, index); } else { current = ast_childlast(parent); } } if(ast_id(current) == TK_EMBEDREF) { args[0] = gen_fieldptr(c, current); set_descriptor(c, t, args[0]); } else { args[0] = gencall_alloc(c, t); } } else { args[0] = gencall_alloc(c, t); } is_new_call = true; ponyint_pool_free_size(tuple_indices.alloc * sizeof(size_t), tuple_indices.data); break; } case TK_BEREF: case TK_FUNREF: case TK_BECHAIN: case TK_FUNCHAIN: args[0] = gen_expr(c, receiver); break; default: pony_assert(0); return NULL; } } else { // Use a null for the receiver type. args[0] = LLVMConstNull(t->use_type); } // Static or virtual dispatch. token_id cap = cap_dispatch(type); reach_method_t* m = reach_method(t, cap, method_name, typeargs); LLVMValueRef func = dispatch_function(c, t, m, args[0]); bool is_message = false; if((ast_id(postfix) == TK_NEWBEREF) || (ast_id(postfix) == TK_BEREF) || (ast_id(postfix) == TK_BECHAIN)) { switch(t->underlying) { case TK_ACTOR: is_message = true; break; case TK_UNIONTYPE: case TK_ISECTTYPE: case TK_INTERFACE: case TK_TRAIT: if(m->cap == TK_TAG) is_message = can_inline_message_send(t, m, method_name); break; default: {} } } // Cast the arguments to the parameter types. LLVMTypeRef f_type = LLVMGetElementType(LLVMTypeOf(func)); LLVMTypeRef* params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size); LLVMGetParamTypes(f_type, params); arg = ast_child(positional); i = 1; LLVMValueRef r = NULL; if(is_message) { // If we're sending a message, trace and send here instead of calling the // sender to trace the most specific types possible. LLVMValueRef* cast_args = (LLVMValueRef*)ponyint_pool_alloc_size(buf_size); cast_args[0] = args[0]; while(arg != NULL) { cast_args[i] = gen_assign_cast(c, params[i], args[i], ast_type(arg)); arg = ast_sibling(arg); i++; } token_id cap = cap_dispatch(type); reach_method_t* m = reach_method(t, cap, method_name, typeargs); codegen_debugloc(c, ast); gen_send_message(c, m, args, cast_args, positional); codegen_debugloc(c, NULL); switch(ast_id(postfix)) { case TK_NEWREF: case TK_NEWBEREF: r = args[0]; break; default: r = c->none_instance; break; } ponyint_pool_free_size(buf_size, cast_args); } else { while(arg != NULL) { args[i] = gen_assign_cast(c, params[i], args[i], ast_type(arg)); arg = ast_sibling(arg); i++; } if(func != NULL) { // If we can error out and we have an invoke target, generate an invoke // instead of a call. codegen_debugloc(c, ast); if(ast_canerror(ast) && (c->frame->invoke_target != NULL)) r = invoke_fun(c, func, args, i, "", true); else r = codegen_call(c, func, args, i); if(is_new_call) { LLVMValueRef md = LLVMMDNodeInContext(c->context, NULL, 0); LLVMSetMetadataStr(r, "pony.newcall", md); } codegen_debugloc(c, NULL); } } // Class constructors return void, expression result is the receiver. if(((ast_id(postfix) == TK_NEWREF) || (ast_id(postfix) == TK_NEWBEREF)) && (t->underlying == TK_CLASS)) r = args[0]; // Chained methods forward their receiver. if((ast_id(postfix) == TK_BECHAIN) || (ast_id(postfix) == TK_FUNCHAIN)) r = args[0]; ponyint_pool_free_size(buf_size, args); ponyint_pool_free_size(buf_size, params); return r; }