static bool check_value(compile_t* c, ast_t* pattern, ast_t* param_type, LLVMValueRef value, LLVMBasicBlockRef next_block) { LLVMValueRef l_value = gen_expr(c, pattern); if(l_value == NULL) return false; gentype_t g; if(!gentype(c, param_type, &g)) return false; LLVMValueRef r_value = gen_assign_cast(c, g.use_type, value, param_type); if(r_value == NULL) return false; LLVMValueRef test = gen_pattern_eq(c, pattern, r_value); if(test == NULL) return false; LLVMBasicBlockRef continue_block = codegen_block(c, "pattern_continue"); LLVMBuildCondBr(c->builder, test, continue_block, next_block); LLVMPositionBuilderAtEnd(c->builder, continue_block); return true; }
LLVMValueRef gen_return(compile_t* c, ast_t* ast) { ast_t* expr = ast_child(ast); LLVMValueRef value = gen_expr(c, expr); size_t clause; ast_t* try_expr = ast_try_clause(ast, &clause); // Do the then block only if we return in the body or else clause. // In the then block, return without doing the then block. if((try_expr != NULL) && (clause != 2)) gen_expr(c, ast_childidx(try_expr, 2)); LLVMTypeRef f_type = LLVMGetElementType(LLVMTypeOf(codegen_fun(c))); LLVMTypeRef r_type = LLVMGetReturnType(f_type); codegen_debugloc(c, ast); if(LLVMGetTypeKind(r_type) != LLVMVoidTypeKind) { LLVMValueRef ret = gen_assign_cast(c, r_type, value, ast_type(expr)); codegen_scope_lifetime_end(c); LLVMBuildRet(c->builder, ret); } else { codegen_scope_lifetime_end(c); LLVMBuildRetVoid(c->builder); } codegen_debugloc(c, NULL); return GEN_NOVALUE; }
LLVMValueRef gen_tuple(compile_t* c, ast_t* ast) { ast_t* child = ast_child(ast); if(ast_sibling(child) == NULL) return gen_expr(c, child); deferred_reification_t* reify = c->frame->reify; ast_t* type = deferred_reify(reify, ast_type(ast), c->opt); // If we contain '_', we have no usable value. if(contains_dontcare(type)) { ast_free_unattached(type); return GEN_NOTNEEDED; } reach_type_t* t = reach_type(c->reach, type); compile_type_t* c_t = (compile_type_t*)t->c_type; int count = LLVMCountStructElementTypes(c_t->primitive); size_t buf_size = count * sizeof(LLVMTypeRef); LLVMTypeRef* elements = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size); LLVMGetStructElementTypes(c_t->primitive, elements); LLVMValueRef tuple = LLVMGetUndef(c_t->primitive); int i = 0; while(child != NULL) { LLVMValueRef value = gen_expr(c, child); if(value == NULL) { ponyint_pool_free_size(buf_size, elements); return NULL; } // We'll have an undefined element if one of our source elements is a // variable declaration. This is ok, since the tuple value will never be // used. if(value == GEN_NOVALUE || value == GEN_NOTNEEDED) { ponyint_pool_free_size(buf_size, elements); return value; } ast_t* child_type = deferred_reify(reify, ast_type(child), c->opt); value = gen_assign_cast(c, elements[i], value, child_type); ast_free_unattached(child_type); tuple = LLVMBuildInsertValue(c->builder, tuple, value, i++, ""); child = ast_sibling(child); } ponyint_pool_free_size(buf_size, elements); return tuple; }
static LLVMValueRef make_arg(compile_t* c, LLVMTypeRef type, ast_t* arg) { LLVMValueRef value = gen_expr(c, arg); if(value == NULL) return NULL; return gen_assign_cast(c, type, value, ast_type(arg)); }
LLVMValueRef gen_localload(compile_t* c, ast_t* ast) { LLVMValueRef local_ptr = gen_localptr(c, ast); if(local_ptr == NULL) return NULL; ast_t* type = deferred_reify(c->frame->reify, ast_type(ast), c->opt); reach_type_t* t = reach_type(c->reach, type); ast_free_unattached(type); compile_type_t* c_t = (compile_type_t*)t->c_type; LLVMValueRef value = LLVMBuildLoad(c->builder, local_ptr, ""); return gen_assign_cast(c, c_t->use_type, value, t->ast_cap); }
LLVMValueRef gen_break(compile_t* c, ast_t* ast) { ast_t* body = ast_child(ast); LLVMBasicBlockRef target; if(ast_id(body) == TK_NONE) { target = c->frame->break_novalue_target; } else { ast_t* body_type = ast_type(body); // Get the break target. target = c->frame->break_target; // Get the phi node. LLVMValueRef post_phi = LLVMGetFirstInstruction(target); bool needed = (post_phi != NULL) && LLVMIsAPHINode(post_phi); // Build the break expression. LLVMValueRef value = gen_expr(c, body); if(needed) { // Cast it to the phi type if we need to. LLVMTypeRef phi_type = LLVMTypeOf(post_phi); value = gen_assign_cast(c, phi_type, value, body_type); } if(value == NULL) return NULL; // Add break value to the post block phi node. if(needed) { LLVMBasicBlockRef insert_block = LLVMGetInsertBlock(c->builder); LLVMAddIncoming(post_phi, &value, &insert_block, 1); } } // Jump to the break target. codegen_scope_lifetime_end(c); codegen_debugloc(c, ast); LLVMBuildBr(c->builder, target); codegen_debugloc(c, NULL); return GEN_NOVALUE; }
static LLVMValueRef assign_one(compile_t* c, LLVMValueRef l_value, LLVMValueRef r_value, ast_t* r_type) { LLVMValueRef result = LLVMBuildLoad(c->builder, l_value, ""); // Cast the rvalue appropriately. LLVMTypeRef l_type = LLVMGetElementType(LLVMTypeOf(l_value)); LLVMValueRef cast_value = gen_assign_cast(c, l_type, r_value, r_type); if(cast_value == NULL) return NULL; // Store to the field. LLVMBuildStore(c->builder, cast_value, l_value); return result; }
static bool genfun_fun(compile_t* c, reachable_type_t* t, reachable_method_t* m) { assert(m->func != NULL); AST_GET_CHILDREN(m->r_fun, cap, id, typeparams, params, result, can_error, body); if(m->name == c->str__final) { t->final_fn = m->func; LLVMSetFunctionCallConv(m->func, LLVMCCallConv); } codegen_startfun(c, m->func, m->di_file, m->di_method); name_params(c, t, m, params, m->func); LLVMValueRef value = gen_expr(c, body); if(value == NULL) return false; if(value != GEN_NOVALUE) { LLVMTypeRef f_type = LLVMGetElementType(LLVMTypeOf(m->func)); LLVMTypeRef r_type = LLVMGetReturnType(f_type); // If the result type is known to be a tuple, do the correct assignment // cast even if the body type is not a tuple. ast_t* body_type = ast_type(body); if(ast_id(result) == TK_TUPLETYPE) body_type = result; LLVMValueRef ret = gen_assign_cast(c, r_type, value, body_type); if(ret == NULL) return false; codegen_debugloc(c, ast_childlast(body)); LLVMBuildRet(c->builder, ret); codegen_debugloc(c, NULL); } codegen_finishfun(c); return true; }
static LLVMValueRef genfun_fun(compile_t* c, gentype_t* g, const char *name, ast_t* typeargs) { ast_t* fun = get_fun(g, name, typeargs); LLVMValueRef func = get_prototype(c, g, name, typeargs, fun); if(func == NULL) { ast_free_unattached(fun); return NULL; } if(LLVMCountBasicBlocks(func) != 0) { ast_free_unattached(fun); return func; } codegen_startfun(c, func, ast_debug(fun)); name_params(c, g->ast, ast_childidx(fun, 3), func); genfun_dwarf(c, g, name, typeargs, fun); ast_t* body = ast_childidx(fun, 6); LLVMValueRef value = gen_expr(c, body); if(value == NULL) { ast_free_unattached(fun); return NULL; } else if(value != GEN_NOVALUE) { genfun_dwarf_return(c, body); LLVMTypeRef f_type = LLVMGetElementType(LLVMTypeOf(func)); LLVMTypeRef r_type = LLVMGetReturnType(f_type); LLVMValueRef ret = gen_assign_cast(c, r_type, value, ast_type(body)); LLVMBuildRet(c->builder, ret); } codegen_finishfun(c); ast_free_unattached(fun); return func; }
static bool check_value(compile_t* c, ast_t* pattern, ast_t* param_type, LLVMValueRef value, LLVMBasicBlockRef next_block) { reach_type_t* t = reach_type(c->reach, param_type); LLVMValueRef r_value = gen_assign_cast(c, t->use_type, value, param_type); if(r_value == NULL) return false; LLVMValueRef result = gen_pattern_eq(c, pattern, r_value); if(result == NULL) return false; LLVMBasicBlockRef continue_block = codegen_block(c, "pattern_continue"); LLVMValueRef test = LLVMBuildTrunc(c->builder, result, c->i1, ""); LLVMBuildCondBr(c->builder, test, continue_block, next_block); LLVMPositionBuilderAtEnd(c->builder, continue_block); return true; }
LLVMValueRef gen_fieldload(compile_t* c, ast_t* ast) { AST_GET_CHILDREN(ast, left, right); LLVMValueRef field = gen_fieldptr(c, ast); if(field == NULL) return NULL; deferred_reification_t* reify = c->frame->reify; ast_t* type = deferred_reify(reify, ast_type(right), c->opt); reach_type_t* t = reach_type(c->reach, type); pony_assert(t != NULL); ast_free_unattached(type); compile_type_t* c_t = (compile_type_t*)t->c_type; field = LLVMBuildLoad(c->builder, field, ""); return gen_assign_cast(c, c_t->use_type, field, t->ast_cap); }
LLVMValueRef gen_tupleelemptr(compile_t* c, ast_t* ast) { AST_GET_CHILDREN(ast, left, right); LLVMValueRef l_value = gen_expr(c, left); if(l_value == NULL) return NULL; deferred_reification_t* reify = c->frame->reify; 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); compile_type_t* c_t = (compile_type_t*)t->c_type; ast_t* l_type = deferred_reify(reify, ast_type(left), c->opt); LLVMValueRef value = make_tupleelemptr(c, l_value, l_type, right); ast_free_unattached(l_type); return gen_assign_cast(c, c_t->use_type, value, t->ast_cap); }
static LLVMValueRef assign_field(compile_t* c, LLVMValueRef l_value, LLVMValueRef r_value, ast_t* p_type, ast_t* r_type) { LLVMValueRef result = LLVMBuildLoad(c->builder, l_value, ""); // Cast the rvalue appropriately. LLVMTypeRef cast_type = LLVMGetElementType(LLVMTypeOf(l_value)); LLVMValueRef cast_value = gen_assign_cast(c, cast_type, r_value, r_type); if(cast_value == NULL) return NULL; // Store to the field. LLVMValueRef store = LLVMBuildStore(c->builder, cast_value, l_value); LLVMValueRef metadata = tbaa_metadata_for_type(c, p_type); const char id[] = "tbaa"; LLVMSetMetadata(result, LLVMGetMDKindID(id, sizeof(id) - 1), metadata); LLVMSetMetadata(store, LLVMGetMDKindID(id, sizeof(id) - 1), metadata); return result; }
static bool case_body(compile_t* c, ast_t* body, LLVMBasicBlockRef post_block, LLVMValueRef phi, LLVMTypeRef phi_type) { LLVMValueRef body_value = gen_expr(c, body); // If it returns, we don't branch to the post block. if(body_value == GEN_NOVALUE) return true; if(is_result_needed(body)) { ast_t* body_type = ast_type(body); body_value = gen_assign_cast(c, phi_type, body_value, body_type); if(body_value == NULL) return false; LLVMBasicBlockRef block = LLVMGetInsertBlock(c->builder); LLVMAddIncoming(phi, &body_value, &block, 1); } LLVMBuildBr(c->builder, post_block); return true; }
static LLVMValueRef assign_to_tuple(compile_t* c, LLVMTypeRef l_type, LLVMValueRef r_value, ast_t* type) { // Cast each component. assert(ast_id(type) == TK_TUPLETYPE); int count = LLVMCountStructElementTypes(l_type); size_t buf_size = count * sizeof(LLVMTypeRef); LLVMTypeRef* elements = (LLVMTypeRef*)pool_alloc_size(buf_size); LLVMGetStructElementTypes(l_type, elements); LLVMValueRef result = LLVMGetUndef(l_type); ast_t* type_child = ast_child(type); int i = 0; while(type_child != NULL) { LLVMValueRef r_child = LLVMBuildExtractValue(c->builder, r_value, i, ""); LLVMValueRef cast_value = gen_assign_cast(c, elements[i], r_child, type_child); if(cast_value == NULL) { pool_free_size(buf_size, elements); return NULL; } result = LLVMBuildInsertValue(c->builder, result, cast_value, i, ""); type_child = ast_sibling(type_child); i++; } pool_free_size(buf_size, elements); return result; }
void gen_send_message(compile_t* c, reach_method_t* m, LLVMValueRef args[], ast_t* args_ast) { // Allocate the message, setting its size and ID. compile_method_t* c_m = (compile_method_t*)m->c_method; size_t msg_size = (size_t)LLVMABISizeOfType(c->target_data, c_m->msg_type); LLVMTypeRef msg_type_ptr = LLVMPointerType(c_m->msg_type, 0); size_t params_buf_size = (m->param_count + 3) * sizeof(LLVMTypeRef); LLVMTypeRef* param_types = (LLVMTypeRef*)ponyint_pool_alloc_size(params_buf_size); LLVMGetStructElementTypes(c_m->msg_type, param_types); size_t args_buf_size = (m->param_count + 1) * sizeof(LLVMValueRef); LLVMValueRef* cast_args = (LLVMValueRef*)ponyint_pool_alloc_size(args_buf_size); size_t arg_types_buf_size = m->param_count * sizeof(ast_t*); ast_t** arg_types = (ast_t**)ponyint_pool_alloc_size(arg_types_buf_size); ast_t* arg_ast = ast_child(args_ast); deferred_reification_t* reify = c->frame->reify; for(size_t i = 0; i < m->param_count; i++) { arg_types[i] = deferred_reify(reify, ast_type(arg_ast), c->opt); cast_args[i+1] = gen_assign_cast(c, param_types[i+3], args[i+1], arg_types[i]); arg_ast = ast_sibling(arg_ast); } LLVMValueRef msg_args[5]; msg_args[0] = LLVMConstInt(c->i32, ponyint_pool_index(msg_size), false); msg_args[1] = LLVMConstInt(c->i32, m->vtable_index, false); LLVMValueRef msg = gencall_runtime(c, "pony_alloc_msg", msg_args, 2, ""); LLVMValueRef md = LLVMMDNodeInContext(c->context, NULL, 0); LLVMSetMetadataStr(msg, "pony.msgsend", md); LLVMValueRef msg_ptr = LLVMBuildBitCast(c->builder, msg, msg_type_ptr, ""); for(unsigned int i = 0; i < m->param_count; i++) { LLVMValueRef arg_ptr = LLVMBuildStructGEP(c->builder, msg_ptr, i + 3, ""); LLVMBuildStore(c->builder, cast_args[i+1], arg_ptr); } // Trace while populating the message contents. bool need_trace = false; for(size_t i = 0; i < m->param_count; i++) { if(gentrace_needed(c, arg_types[i], m->params[i].ast)) { need_trace = true; break; } } LLVMValueRef ctx = codegen_ctx(c); if(need_trace) { LLVMValueRef gc = gencall_runtime(c, "pony_gc_send", &ctx, 1, ""); LLVMSetMetadataStr(gc, "pony.msgsend", md); for(size_t i = 0; i < m->param_count; i++) { gentrace(c, ctx, args[i+1], cast_args[i+1], arg_types[i], m->params[i].ast); } gc = gencall_runtime(c, "pony_send_done", &ctx, 1, ""); LLVMSetMetadataStr(gc, "pony.msgsend", md); } // Send the message. msg_args[0] = ctx; msg_args[1] = LLVMBuildBitCast(c->builder, args[0], c->object_ptr, ""); msg_args[2] = msg; msg_args[3] = msg; msg_args[4] = LLVMConstInt(c->i1, 1, false); LLVMValueRef send; if(ast_id(m->fun->ast) == TK_NEW) send = gencall_runtime(c, "pony_sendv_single", msg_args, 5, ""); else send = gencall_runtime(c, "pony_sendv", msg_args, 5, ""); LLVMSetMetadataStr(send, "pony.msgsend", md); ponyint_pool_free_size(params_buf_size, param_types); ponyint_pool_free_size(args_buf_size, cast_args); for(size_t i = 0; i < m->param_count; i++) ast_free_unattached(arg_types[i]); ponyint_pool_free_size(arg_types_buf_size, arg_types); }
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, postfix, positional, named, question); AST_GET_CHILDREN(postfix, receiver, method); ast_t* typeargs = NULL; deferred_reification_t* reify = c->frame->reify; // 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 = deferred_reify(reify, method, c->opt); AST_GET_CHILDREN_NO_DECL(receiver, receiver, method); break; default: {} } // Get the receiver type. const char* method_name = ast_name(method); ast_t* type = deferred_reify(reify, ast_type(receiver), c->opt); reach_type_t* t = reach_type(c->reach, type); pony_assert(t != NULL); token_id cap = cap_dispatch(type); reach_method_t* m = reach_method(t, cap, method_name, typeargs); ast_free_unattached(type); ast_free_unattached(typeargs); // Generate the arguments. size_t count = m->param_count + 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: args[0] = gen_constructor_receiver(c, t, ast); is_new_call = true; 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(((compile_type_t*)t->c_type)->use_type); } // Static or virtual dispatch. 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: {} } } bool bare = m->cap == TK_AT; 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. codegen_debugloc(c, ast); gen_send_message(c, m, 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; } } else { LLVMTypeRef f_type = LLVMGetElementType(LLVMTypeOf(func)); LLVMTypeRef* params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size); LLVMGetParamTypes(f_type, params + (bare ? 1 : 0)); arg = ast_child(positional); i = 1; while(arg != NULL) { ast_t* arg_type = deferred_reify(reify, ast_type(arg), c->opt); args[i] = gen_assign_cast(c, params[i], args[i], arg_type); ast_free_unattached(arg_type); arg = ast_sibling(arg); i++; } uintptr_t arg_offset = 0; if(bare) { arg_offset = 1; 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 + arg_offset, i, "", !bare); else r = codegen_call(c, func, args + arg_offset, i, !bare); if(is_new_call) { LLVMValueRef md = LLVMMDNodeInContext(c->context, NULL, 0); LLVMSetMetadataStr(r, "pony.newcall", md); } codegen_debugloc(c, NULL); ponyint_pool_free_size(buf_size, params); } } // Bare methods with None return type return void, special case a None return // value. if(bare && is_none(m->result->ast)) r = c->none_instance; // 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); return r; }
static bool gen_field_init(compile_t* c, gentype_t* g) { LLVMValueRef this_ptr = LLVMGetParam(codegen_fun(c), 0); ast_t* def = (ast_t*)ast_data(g->ast); ast_t* members = ast_childidx(def, 4); ast_t* member = ast_child(members); // Struct index of the current field. int index = 1; if(ast_id(def) == TK_ACTOR) index++; // Iterate through all fields. while(member != NULL) { switch(ast_id(member)) { case TK_FVAR: case TK_FLET: { // Skip this field if it has no initialiser. AST_GET_CHILDREN(member, id, type, body); if(ast_id(body) != TK_NONE) { // Reify the initialiser. ast_t* this_type = set_cap_and_ephemeral(g->ast, TK_REF, TK_NONE); ast_t* var = lookup(NULL, NULL, this_type, ast_name(id)); ast_free_unattached(this_type); assert(var != NULL); body = ast_childidx(var, 2); // Get the field pointer. dwarf_location(&c->dwarf, body); LLVMValueRef l_value = LLVMBuildStructGEP(c->builder, this_ptr, index, ""); // Cast the initialiser to the field type. LLVMValueRef r_value = gen_expr(c, body); if(r_value == NULL) return false; LLVMTypeRef l_type = LLVMGetElementType(LLVMTypeOf(l_value)); LLVMValueRef cast_value = gen_assign_cast(c, l_type, r_value, ast_type(body)); if(cast_value == NULL) return false; // Store the result. LLVMBuildStore(c->builder, cast_value, l_value); } index++; break; } default: {} } member = ast_sibling(member); } return true; }
LLVMValueRef gen_while(compile_t* c, ast_t* ast) { bool needed = is_result_needed(ast); AST_GET_CHILDREN(ast, cond, body, else_clause); ast_t* type = ast_type(ast); ast_t* body_type = ast_type(body); ast_t* else_type = ast_type(else_clause); reach_type_t* phi_type = NULL; if(needed && !is_control_type(type)) phi_type = reach_type(c->reach, type); LLVMBasicBlockRef init_block = codegen_block(c, "while_init"); LLVMBasicBlockRef body_block = codegen_block(c, "while_body"); LLVMBasicBlockRef else_block = codegen_block(c, "while_else"); LLVMBasicBlockRef post_block = NULL; LLVMBuildBr(c->builder, init_block); // start the post block so that a break can modify the phi node LLVMValueRef phi = GEN_NOTNEEDED; if(!is_control_type(type)) { // Start the post block so that a break can modify the phi node. post_block = codegen_block(c, "while_post"); LLVMPositionBuilderAtEnd(c->builder, post_block); if(needed) phi = LLVMBuildPhi(c->builder, phi_type->use_type, ""); } // Push the loop status. codegen_pushloop(c, init_block, post_block, else_block); // init // This jumps either to the body or the else clause. This is not evaluated // on each loop iteration: only on the first entry or after a continue. LLVMPositionBuilderAtEnd(c->builder, init_block); LLVMValueRef i_value = gen_expr(c, cond); if(i_value == NULL) return NULL; LLVMValueRef test = LLVMBuildTrunc(c->builder, i_value, c->i1, ""); LLVMBuildCondBr(c->builder, test, body_block, else_block); // Body. LLVMPositionBuilderAtEnd(c->builder, body_block); LLVMValueRef l_value = gen_expr(c, body); if(needed) l_value = gen_assign_cast(c, phi_type->use_type, l_value, body_type); if(l_value == NULL) return NULL; LLVMBasicBlockRef body_from = NULL; // If the body can't result in a value, don't generate the conditional // evaluation. This basic block for the body already has a terminator. if(l_value != GEN_NOVALUE) { // The body evaluates the condition itself, jumping either back to the body // or directly to the post block. LLVMValueRef c_value = gen_expr(c, cond); if(c_value == NULL) return NULL; body_from = LLVMGetInsertBlock(c->builder); LLVMValueRef test = LLVMBuildTrunc(c->builder, c_value, c->i1, ""); LLVMBuildCondBr(c->builder, test, body_block, post_block); } // Don't need loop status for the else block. codegen_poploop(c); // else // If the loop doesn't generate a value (doesn't execute, or continues on the // last iteration), the else clause generates the value. LLVMPositionBuilderAtEnd(c->builder, else_block); LLVMValueRef r_value = gen_expr(c, else_clause); LLVMBasicBlockRef else_from = NULL; if(r_value != GEN_NOVALUE) { if(r_value == NULL) return NULL; if(needed) r_value = gen_assign_cast(c, phi_type->use_type, r_value, else_type); else_from = LLVMGetInsertBlock(c->builder); LLVMBuildBr(c->builder, post_block); } if(is_control_type(type)) return GEN_NOVALUE; // post LLVMPositionBuilderAtEnd(c->builder, post_block); if(needed) { if(l_value != GEN_NOVALUE) LLVMAddIncoming(phi, &l_value, &body_from, 1); if(r_value != GEN_NOVALUE) LLVMAddIncoming(phi, &r_value, &else_from, 1); return phi; } return GEN_NOTNEEDED; }
LLVMValueRef gen_repeat(compile_t* c, ast_t* ast) { bool needed = is_result_needed(ast); AST_GET_CHILDREN(ast, body, cond, else_clause); ast_t* type = ast_type(ast); ast_t* body_type = ast_type(body); ast_t* else_type = ast_type(else_clause); reach_type_t* phi_type = NULL; if(needed && !is_control_type(type)) phi_type = reach_type(c->reach, type); LLVMBasicBlockRef body_block = codegen_block(c, "repeat_body"); LLVMBasicBlockRef cond_block = codegen_block(c, "repeat_cond"); LLVMBasicBlockRef else_block = codegen_block(c, "repeat_else"); LLVMBasicBlockRef post_block = NULL; LLVMBuildBr(c->builder, body_block); // start the post block so that a break can modify the phi node LLVMValueRef phi = GEN_NOTNEEDED; if(!is_control_type(type)) { // Start the post block so that a break can modify the phi node. post_block = codegen_block(c, "repeat_post"); LLVMPositionBuilderAtEnd(c->builder, post_block); if(needed) phi = LLVMBuildPhi(c->builder, phi_type->use_type, ""); } // Push the loop status. codegen_pushloop(c, cond_block, post_block, else_block); // Body. LLVMPositionBuilderAtEnd(c->builder, body_block); LLVMValueRef value = gen_expr(c, body); if(needed) value = gen_assign_cast(c, phi_type->use_type, value, body_type); if(value == NULL) return NULL; LLVMBasicBlockRef body_from = NULL; // If the body can't result in a value, don't generate the conditional // evaluation. This basic block for the body already has a terminator. if(value != GEN_NOVALUE) { // The body evaluates the condition itself, jumping either back to the body // or directly to the post block. LLVMValueRef c_value = gen_expr(c, cond); if(c_value == NULL) return NULL; body_from = LLVMGetInsertBlock(c->builder); LLVMValueRef test = LLVMBuildTrunc(c->builder, c_value, c->i1, ""); LLVMBuildCondBr(c->builder, test, post_block, body_block); } // cond block // This is only evaluated from a continue, jumping either back to the body // or to the else block. LLVMPositionBuilderAtEnd(c->builder, cond_block); LLVMValueRef i_value = gen_expr(c, cond); LLVMValueRef test = LLVMBuildTrunc(c->builder, i_value, c->i1, ""); LLVMBuildCondBr(c->builder, test, else_block, body_block); // Don't need loop status for the else block. codegen_poploop(c); // else // Only happens for a continue in the last iteration. LLVMPositionBuilderAtEnd(c->builder, else_block); LLVMValueRef else_value = gen_expr(c, else_clause); LLVMBasicBlockRef else_from = NULL; if(else_value == NULL) return NULL; if(needed) else_value = gen_assign_cast(c, phi_type->use_type, else_value, else_type); if(else_value != GEN_NOVALUE) { else_from = LLVMGetInsertBlock(c->builder); LLVMBuildBr(c->builder, post_block); } if(is_control_type(type)) return GEN_NOVALUE; // post LLVMPositionBuilderAtEnd(c->builder, post_block); if(needed) { if(value != GEN_NOVALUE) LLVMAddIncoming(phi, &value, &body_from, 1); if(else_value != GEN_NOVALUE) LLVMAddIncoming(phi, &else_value, &else_from, 1); return phi; } return GEN_NOTNEEDED; }
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; }
LLVMValueRef gen_if(compile_t* c, ast_t* ast) { bool needed = is_result_needed(ast); ast_t* type = ast_type(ast); AST_GET_CHILDREN(ast, cond, left, right); ast_t* left_type = ast_type(left); ast_t* right_type = ast_type(right); // We will have no type if both branches have return statements. reach_type_t* phi_type = NULL; if(!is_control_type(type)) phi_type = reach_type(c->reach, type); LLVMValueRef c_value = gen_expr(c, cond); if(c_value == NULL) return NULL; // If the conditional is constant, generate only one branch. bool gen_left = true; bool gen_right = true; if(LLVMIsAConstantInt(c_value)) { int value = (int)LLVMConstIntGetZExtValue(c_value); if(value == 0) gen_left = false; else gen_right = false; } LLVMBasicBlockRef then_block = codegen_block(c, "if_then"); LLVMBasicBlockRef else_block = codegen_block(c, "if_else"); LLVMBasicBlockRef post_block = NULL; // If both branches return, we have no post block. if(!is_control_type(type)) post_block = codegen_block(c, "if_post"); LLVMValueRef test = LLVMBuildTrunc(c->builder, c_value, c->i1, ""); LLVMBuildCondBr(c->builder, test, then_block, else_block); // Left branch. LLVMPositionBuilderAtEnd(c->builder, then_block); LLVMValueRef l_value; if(gen_left) { l_value = gen_expr(c, left); } else if(phi_type != NULL) { l_value = LLVMConstNull(phi_type->use_type); } else { LLVMBuildUnreachable(c->builder); l_value = GEN_NOVALUE; } if(l_value != GEN_NOVALUE) { if(needed) l_value = gen_assign_cast(c, phi_type->use_type, l_value, left_type); if(l_value == NULL) return NULL; then_block = LLVMGetInsertBlock(c->builder); LLVMBuildBr(c->builder, post_block); } // Right branch. LLVMPositionBuilderAtEnd(c->builder, else_block); LLVMValueRef r_value; if(gen_right) { r_value = gen_expr(c, right); } else if(phi_type != NULL) { r_value = LLVMConstNull(phi_type->use_type); } else { LLVMBuildUnreachable(c->builder); r_value = GEN_NOVALUE; } // If the right side returns, we don't branch to the post block. if(r_value != GEN_NOVALUE) { if(needed) r_value = gen_assign_cast(c, phi_type->use_type, r_value, right_type); if(r_value == NULL) return NULL; else_block = LLVMGetInsertBlock(c->builder); LLVMBuildBr(c->builder, post_block); } // If both sides return, we return a sentinal value. if(is_control_type(type)) return GEN_NOVALUE; // Continue in the post block. LLVMPositionBuilderAtEnd(c->builder, post_block); if(needed) { LLVMValueRef phi = LLVMBuildPhi(c->builder, phi_type->use_type, ""); if(l_value != GEN_NOVALUE) LLVMAddIncoming(phi, &l_value, &then_block, 1); if(r_value != GEN_NOVALUE) LLVMAddIncoming(phi, &r_value, &else_block, 1); return phi; } return GEN_NOTNEEDED; }
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; }
LLVMValueRef gen_try(compile_t* c, ast_t* ast) { bool needed = is_result_needed(ast); AST_GET_CHILDREN(ast, body, else_clause, then_clause); ast_t* type = ast_type(ast); ast_t* body_type = ast_type(body); ast_t* else_type = ast_type(else_clause); reach_type_t* phi_type = NULL; // We will have no type if both branches have return statements. if(!is_control_type(type)) phi_type = reach_type(c->reach, type); LLVMBasicBlockRef block = LLVMGetInsertBlock(c->builder); LLVMBasicBlockRef else_block = codegen_block(c, "try_else"); LLVMBasicBlockRef post_block = NULL; if(!is_control_type(type)) post_block = codegen_block(c, "try_post"); // Keep a reference to the else block. codegen_pushtry(c, else_block); // Body block. LLVMPositionBuilderAtEnd(c->builder, block); LLVMValueRef body_value = gen_expr(c, body); if(body_value != GEN_NOVALUE) { if(needed) { body_value = gen_assign_cast(c, phi_type->use_type, body_value, body_type); } if(body_value == NULL) return NULL; gen_expr(c, then_clause); block = LLVMGetInsertBlock(c->builder); LLVMBuildBr(c->builder, post_block); } // Pop the try before generating the else block. codegen_poptry(c); // Else block. LLVMPositionBuilderAtEnd(c->builder, else_block); // The landing pad is marked as a cleanup, since exceptions are typeless and // valueless. The first landing pad is always the destination. LLVMTypeRef lp_elements[2]; lp_elements[0] = c->void_ptr; lp_elements[1] = c->i32; LLVMTypeRef lp_type = LLVMStructTypeInContext(c->context, lp_elements, 2, false); #if PONY_LLVM == 307 && LLVM_VERSION_PATCH == 0 // This backwards-incompatible API change to LLVMBuildLandingPad is only in // LLVM 3.7.0. In 3.7.1 and all later versions, backward-compatibility was // restored. assert((c->frame->fun != NULL) && "No function in current frame!"); LLVMSetPersonalityFn(c->frame->fun, c->personality); LLVMValueRef landing = LLVMBuildLandingPad(c->builder, lp_type, 1, ""); #else LLVMValueRef landing = LLVMBuildLandingPad(c->builder, lp_type, c->personality, 1, ""); #endif LLVMAddClause(landing, LLVMConstNull(c->void_ptr)); LLVMValueRef else_value = gen_expr(c, else_clause); if(else_value != GEN_NOVALUE) { if(needed) { else_value = gen_assign_cast(c, phi_type->use_type, else_value, else_type); } if(else_value == NULL) return NULL; gen_expr(c, then_clause); else_block = LLVMGetInsertBlock(c->builder); LLVMBuildBr(c->builder, post_block); } // If both sides return, we return a sentinal value. if(is_control_type(type)) return GEN_NOVALUE; // Continue in the post block. LLVMPositionBuilderAtEnd(c->builder, post_block); if(needed) { LLVMValueRef phi = LLVMBuildPhi(c->builder, phi_type->use_type, ""); if(body_value != GEN_NOVALUE) LLVMAddIncoming(phi, &body_value, &block, 1); if(else_value != GEN_NOVALUE) LLVMAddIncoming(phi, &else_value, &else_block, 1); return phi; } return GEN_NOTNEEDED; }