IMODE *genstmt(STATEMENT *stmt, SYMBOL *funcsp) /* * genstmt will generate a statement and follow the next pointer * until the block is generated. */ { IMODE *rv = NULL; while (stmt != 0) { STATEMENT *last = stmt; switch (stmt->type) { case st_varstart: gen_varstart(stmt->select); break; case st_dbgblock: gen_dbgblock(stmt->label); break; break; case st_block: rv = genstmt(stmt->lower, funcsp); genstmt(stmt->blockTail, funcsp); break; case st_label: gen_label((int)stmt->label); break; case st_goto: gen_igoto(i_goto, (int)stmt->label); break; case st_asmgoto: gen_igoto(i_asmgoto, (int)stmt->label); break; case st_asmcond: gen_igoto(i_asmcond, (int)stmt->label); break; case st_try: gen_try(funcsp, stmt, stmt->label, stmt->endlabel, stmt->breaklabel, stmt->lower); break; case st_catch: { STATEMENT *last; while (stmt && stmt->type == st_catch) { gen_catch(funcsp, stmt, stmt->altlabel, stmt->breaklabel, stmt->lower); last = stmt; stmt = stmt->next; } stmt = last; gen_label(stmt->breaklabel); } break; case st_expr: case st_declare: if (stmt->select) rv = gen_expr(funcsp, stmt->select, F_NOVALUE, natural_size(stmt->select)); break; case st_return: genreturn(stmt, funcsp, 0, 0, NULL); break; case st_line: gen_line(stmt->lineData); break; case st_select: genselect(stmt, funcsp, TRUE); break; case st_notselect: genselect(stmt, funcsp, FALSE); break; case st_switch: genxswitch(stmt, funcsp); break; case st__genword: gen_genword(stmt, funcsp); break; case st_passthrough: gen_asm(stmt); break; case st_datapassthrough: gen_asmdata(stmt); break; default: diag("unknown statement."); break; } if (last->type != st_return && last->destexp) { gen_expr(funcsp, last->destexp, F_NOVALUE, ISZ_ADDR); } stmt = stmt->next; } return rv; }
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; }
/* Recursively generate code for function */ static void gen_expr(node_ptr expr) { node_ptr ele; switch(expr->type) { case N_QUOTE: yyserror("Unexpected quoted string", expr->sval); break; case N_VAR: { node_ptr qstring = find_symbol(expr->sval); if (qstring) #if defined(VLOG) || defined(UCLID) outgen_print("%s", expr->sval); #else outgen_print("(%s)", qstring->sval); #endif else yyserror("Invalid variable '%s'", expr->sval); #ifdef UCLID check_for_arg(expr->sval); #endif } break; case N_NUM: #ifdef UCLID { long long int val = atoll(expr->sval); if (val < -1) outgen_print("pred^%d(CZERO)", -val); else if (val == -1) outgen_print("pred(CZERO)"); else if (val == 0) outgen_print("CZERO"); else if (val == 1) outgen_print("succ(CZERO)"); else outgen_print("succ^%d(CZERO)", val); } #else /* !UCLID */ fputs(expr->sval, outfile); #endif /* UCLID */ break; case N_AND: outgen_print("("); outgen_upindent(); gen_expr(expr->arg1); outgen_print(" & "); gen_expr(expr->arg2); outgen_print(")"); outgen_downindent(); break; case N_OR: outgen_print("("); outgen_upindent(); gen_expr(expr->arg1); outgen_print(" | "); gen_expr(expr->arg2); outgen_print(")"); outgen_downindent(); break; case N_NOT: #if defined(VLOG) || defined(UCLID) outgen_print("~"); #else outgen_print("!"); #endif gen_expr(expr->arg1); break; case N_COMP: outgen_print("("); outgen_upindent(); gen_expr(expr->arg1); #ifdef UCLID { char *cval = expr->sval; if (strcmp(cval, "==") == 0) cval = "="; outgen_print(" %s ", cval); } #else /* !UCLID */ outgen_print(" %s ", expr->sval); #endif /* UCLID */ gen_expr(expr->arg2); outgen_print(")"); outgen_downindent(); break; case N_ELE: outgen_print("("); outgen_upindent(); for (ele = expr->arg2; ele; ele=ele->next) { gen_expr(expr->arg1); #ifdef UCLID outgen_print(" = "); #else outgen_print(" == "); #endif gen_expr(ele); if (ele->next) #if defined(VLOG) || defined(UCLID) outgen_print(" | "); #else outgen_print(" || "); #endif } outgen_print(")"); outgen_downindent(); break; case N_CASE: #ifdef UCLID outgen_print("case"); outgen_terminate(); { /* Use this to keep track of last case when no default is given */ node_ptr last_arg2 = NULL; for (ele = expr; ele; ele=ele->next) { outgen_print(" "); if (ele->arg1->type == N_NUM && atoll(ele->arg1->sval) == 1) { outgen_print("default"); last_arg2 = NULL; } else { gen_expr(ele->arg1); last_arg2 = ele->arg2; } outgen_print(" : "); gen_expr(ele->arg2); outgen_print(";"); outgen_terminate(); } if (last_arg2) { /* Use final case as default */ outgen_print(" default : "); gen_expr(last_arg2); outgen_print(";"); outgen_terminate(); } } outgen_print(" esac"); #else /* !UCLID */ outgen_print("("); outgen_upindent(); int done = 0; for (ele = expr; ele && !done; ele=ele->next) { if (ele->arg1->type == N_NUM && atoll(ele->arg1->sval) == 1) { gen_expr(ele->arg2); done = 1; } else { gen_expr(ele->arg1); outgen_print(" ? "); gen_expr(ele->arg2); outgen_print(" : "); } } if (!done) outgen_print("0"); outgen_print(")"); outgen_downindent(); #endif break; default: yyerror("Unknown node type"); break; }
LLVMValueRef make_divmod(compile_t* c, ast_t* left, ast_t* right, const_binop const_f, const_binop const_ui, const_binop const_si, build_binop build_f, build_binop build_ui, build_binop build_si) { ast_t* type = ast_type(left); bool sign = is_signed(c->opt, type); LLVMValueRef l_value = gen_expr(c, left); LLVMValueRef r_value = gen_expr(c, right); if((l_value == NULL) || (r_value == NULL)) return NULL; // TODO: This doesn't pick up `x / 0` for 128 bit numbers on platforms // without native 128 bit support. if(!is_fp(r_value) && LLVMIsConstant(r_value) && (LLVMConstIntGetSExtValue(r_value) == 0) ) { ast_error(right, "constant divide or mod by zero"); return NULL; } if(LLVMIsConstant(l_value) && LLVMIsConstant(r_value)) { if(is_fp(l_value)) return const_f(l_value, r_value); if(sign) return const_si(l_value, r_value); return const_ui(l_value, r_value); } if(is_fp(l_value)) return build_f(c->builder, l_value, r_value, ""); // Setup additional blocks. LLVMBasicBlockRef insert = LLVMGetInsertBlock(c->builder); LLVMBasicBlockRef then_block = codegen_block(c, "div_then"); LLVMBasicBlockRef post_block = codegen_block(c, "div_post"); // Check for div by zero. LLVMTypeRef r_type = LLVMTypeOf(r_value); LLVMValueRef zero = LLVMConstInt(r_type, 0, false); LLVMValueRef cmp = LLVMBuildICmp(c->builder, LLVMIntNE, r_value, zero, ""); LLVMBuildCondBr(c->builder, cmp, then_block, post_block); // Divisor is not zero. LLVMPositionBuilderAtEnd(c->builder, then_block); LLVMValueRef result; if(sign) result = build_si(c->builder, l_value, r_value, ""); else result = build_ui(c->builder, l_value, r_value, ""); LLVMBuildBr(c->builder, post_block); // Phi node. LLVMPositionBuilderAtEnd(c->builder, post_block); LLVMValueRef phi = LLVMBuildPhi(c->builder, r_type, ""); LLVMAddIncoming(phi, &zero, &insert, 1); LLVMAddIncoming(phi, &result, &then_block, 1); return phi; }
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: case TK_EMBED: { // 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); // TODO: embed fields // 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_identity(compile_t* c, ast_t* ast) { ast_t* expr = ast_child(ast); LLVMValueRef value = gen_expr(c, expr); return gen_identity_from_value(c, value); }
LLVMValueRef make_short_circuit(compile_t* c, ast_t* left, ast_t* right, bool is_and) { LLVMBasicBlockRef entry_block = LLVMGetInsertBlock(c->builder); LLVMBasicBlockRef left_block = codegen_block(c, "sc_left"); LLVMValueRef branch = LLVMBuildBr(c->builder, left_block); LLVMPositionBuilderAtEnd(c->builder, left_block); LLVMValueRef l_value = gen_expr(c, left); if(l_value == NULL) return NULL; if(is_constant_i1(c, l_value)) { LLVMInstructionEraseFromParent(branch); LLVMDeleteBasicBlock(left_block); LLVMPositionBuilderAtEnd(c->builder, entry_block); if(is_and) { if(is_always_false(c, l_value)) return gen_expr(c, left); } else { if(is_always_true(c, l_value)) return gen_expr(c, left); } return gen_expr(c, right); } LLVMBasicBlockRef left_exit_block = LLVMGetInsertBlock(c->builder); LLVMBasicBlockRef right_block = codegen_block(c, "sc_right"); LLVMBasicBlockRef post_block = codegen_block(c, "sc_post"); if(is_and) LLVMBuildCondBr(c->builder, l_value, right_block, post_block); else LLVMBuildCondBr(c->builder, l_value, post_block, right_block); LLVMPositionBuilderAtEnd(c->builder, right_block); LLVMValueRef r_value = gen_expr(c, right); if(r_value == NULL) return NULL; LLVMBasicBlockRef right_exit_block = LLVMGetInsertBlock(c->builder); LLVMBuildBr(c->builder, post_block); LLVMPositionBuilderAtEnd(c->builder, post_block); LLVMValueRef phi = LLVMBuildPhi(c->builder, c->i1, ""); LLVMAddIncoming(phi, &l_value, &left_exit_block, 1); LLVMAddIncoming(phi, &r_value, &right_exit_block, 1); if(is_constant_i1(c, r_value)) { if(is_and) { if(is_always_false(c, r_value)) return r_value; } else { if(is_always_true(c, r_value)) return r_value; } return l_value; } return phi; }
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_pattern_eq(compile_t* c, ast_t* pattern, LLVMValueRef r_value) { // This is used for structural equality in pattern matching. ast_t* pattern_type = deferred_reify(c->frame->reify, ast_type(pattern), c->opt); 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) ) { ast_free_unattached(pattern_type); 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); ast_free_unattached(pattern_type); 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, true); codegen_debugloc(c, NULL); return result; }
const out_val *gen_expr_style_assign(const expr *e, out_ctx *octx) { IGNORE_PRINTGEN(gen_expr(e->lhs, octx)); stylef(" = "); return gen_expr(e->rhs, octx); }
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; }
LLVMValueRef gen_funptr(compile_t* c, ast_t* ast) { pony_assert((ast_id(ast) == TK_FUNREF) || (ast_id(ast) == TK_BEREF)); AST_GET_CHILDREN(ast, receiver, method); ast_t* typeargs = NULL; // Dig through function qualification. switch(ast_id(receiver)) { case TK_BEREF: case TK_FUNREF: typeargs = method; AST_GET_CHILDREN_NO_DECL(receiver, receiver, method); break; default: {} } // Generate the receiver. LLVMValueRef value = gen_expr(c, receiver); // Get the receiver type. ast_t* type = deferred_reify(c->frame->reify, ast_type(receiver), c->opt); reach_type_t* t = reach_type(c->reach, type); pony_assert(t != NULL); const char* name = ast_name(method); token_id cap = cap_dispatch(type); reach_method_t* m = reach_method(t, cap, name, typeargs); LLVMValueRef funptr = dispatch_function(c, t, m, value); ast_free_unattached(type); if((m->cap != TK_AT) && (c->linkage != LLVMExternalLinkage)) { // We must reset the function linkage and calling convention since we're // passing a function pointer to a FFI call. Bare methods always use the // external linkage and the C calling convention so we don't need to process // them. switch(t->underlying) { case TK_PRIMITIVE: case TK_STRUCT: case TK_CLASS: case TK_ACTOR: { compile_method_t* c_m = (compile_method_t*)m->c_method; LLVMSetFunctionCallConv(c_m->func, LLVMCCallConv); LLVMSetLinkage(c_m->func, LLVMExternalLinkage); break; } case TK_UNIONTYPE: case TK_ISECTTYPE: case TK_INTERFACE: case TK_TRAIT: set_method_external_interface(t, name, m->vtable_index); break; default: pony_assert(0); break; } } return funptr; }
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; }
void genfunc(SYMBOL *funcsp) /* * generate a function body and dump the icode */ { IMODE *allocaAP = NULL; SYMBOL *oldCurrentFunc; EXPRESSION *funcexp = varNode(en_global, funcsp); SYMBOL *tmpl = funcsp; if (total_errors) return; while (tmpl) if (tmpl->templateLevel) break; else tmpl = tmpl->parentClass; // //printf("%s\n", funcsp->name); temporarySymbols = NULL; contlab = breaklab = - 1; structret_imode = 0 ; tempCount = 0; blockCount = 0; blockMax = 0; exitBlock = 0; consIndex = 0; oldCurrentFunc = theCurrentFunc; theCurrentFunc = funcsp; iexpr_func_init(); if (funcsp->xc && funcsp->xc->xctab) { EXPRESSION *exp; xcexp = varNode(en_auto, funcsp->xc->xctab); xcexp = exprNode(en_add, xcexp, intNode(en_c_i, (LLONG_TYPE)&(((struct _xctab *)0)->funcIndex))); deref(&stdpointer, &xcexp); exp = intNode(en_c_i, 0); xcexp = exprNode(en_assign, xcexp, exp); } else { xcexp = NULL; } /* firstlabel = nextLabel;*/ cseg(); gen_line(funcsp->linedata); gen_func(funcexp, 1); /* in C99 inlines can clash if declared 'extern' in multiple modules */ /* in C++ we introduce virtual functions that get coalesced at link time */ if (cparams.prm_cplusplus && (funcsp->linkage == lk_inline || tmpl)) gen_virtual(funcsp, FALSE); else { if (funcsp->storage_class == sc_global || (funcsp->storage_class == sc_member || funcsp->storage_class == sc_virtual) && funcsp->inlineFunc.stmt) globaldef(funcsp); else localdef(funcsp); gen_strlab(funcsp); /* name of function */ } addblock( - 1); if (funcsp->linkage == lk_interrupt || funcsp->linkage == lk_fault) { gen_icode(i_pushcontext, 0,0,0); /* if (funcsp->loadds) */ /* gen_icode(i_loadcontext, 0,0,0); */ } gen_icode(i_prologue,0,0,0); gen_label(startlab); if (cparams.prm_xcept && funcsp->xc && funcsp->xc->xcInitializeFunc) { gen_expr(funcsp, funcsp->xc->xcInitializeFunc, F_NOVALUE, ISZ_UINT); gen_label(funcsp->xc->xcInitLab); } /* if (funcsp->loadds && funcsp->farproc) */ /* gen_icode(i_loadcontext, 0,0,0); */ AllocateLocalContext(NULL, funcsp); if (funcsp->allocaUsed) { EXPRESSION *allocaExp = anonymousVar(sc_auto, &stdpointer); allocaAP = gen_expr(funcsp, allocaExp, 0, ISZ_ADDR); gen_icode(i_savestack, 0, allocaAP, 0); } /* Generate the icode */ /* LCSE is done while code is generated */ genstmt(funcsp->inlineFunc.stmt->lower, funcsp); if (funcsp->inlineFunc.stmt->blockTail) { gen_icode(i_functailstart, 0, 0, 0); genstmt(funcsp->inlineFunc.stmt->blockTail, funcsp); gen_icode(i_functailend, 0, 0, 0); } genreturn(0, funcsp, 1, 0, allocaAP); gen_func(funcexp, 0); tFree(); InsertParameterThunks(funcsp, blockArray[1]); optimize(funcsp); FreeLocalContext(NULL, funcsp); AllocateStackSpace(funcsp); FillInPrologue(intermed_head, funcsp); /* Code gen from icode */ rewrite_icode(); /* Translate to machine code & dump */ if (chosenAssembler->gen->post_function_gen) chosenAssembler->gen->post_function_gen(funcsp, intermed_head); if (cparams.prm_cplusplus && (funcsp->linkage == lk_inline || tmpl)) gen_endvirtual(funcsp); XTDumpTab(funcsp); intermed_head = NULL; dag_rundown(); oFree(); theCurrentFunc = oldCurrentFunc; if (blockCount > maxBlocks) maxBlocks = blockCount; if (tempCount > maxTemps) maxTemps = tempCount; }
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; }
void gen_expr_cast(expr *e, symtable *stab) { /* ignore the lhs, it's just a type spec */ /* FIXME: size changing? */ gen_expr(e->expr, stab); }
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; }
void loadregs(void) /* * Initailze allocated regs * */ { CSE *csp; ENODE *exptr; unsigned mask, rmask,i,fmask,frmask,size; AMODE *ap, *ap2; csp = olist; while( csp != 0 ) { int sz; if( csp->reg != -1 ) { /* see if preload needed */ exptr = csp->exp; if( !lvalue(exptr) || ((SYM *)exptr->v.p[0]->v.p[0])->funcparm ) { exptr = csp->exp; initstack(); sz = csp->size; ap = gen_expr(exptr,F_ALL,sz); if (sz == 0 && ap->mode == am_immed) sz = 4; if( csp->reg < 16 ) { if (sz == 0 && ap->mode == am_immed) sz = 4; if (ap->mode == am_dreg) peep_tail->oper2->preg = csp->reg; else { ap2 = makedreg(csp->reg); gen_code(op_move,sz,ap,ap2); do_extend(ap2,sz,4,F_DREG); } } else if (csp->reg < 32) { if (sz == 0 && ap->mode == am_immed) sz = 4; if (ap->mode == am_areg) peep_tail->oper2->preg = csp->reg - 16; else { ap2 = makeareg(csp->reg - 16); gen_code(op_move,4,ap,ap2); } } else { if (sz == 0 && ap->mode == am_immed) sz = 8; if (ap->mode == am_freg) peep_tail->oper2->preg = csp->reg - 32; else { ap2 = makefreg(csp->reg - 32); size = 8; if (exptr->nodetype == en_floatref) size = 6; gen_code(op_fmove,size,ap,ap2); } } freeop(ap); if (lvalue(exptr) && ((SYM *)exptr->v.p[0]->v.p[0])->funcparm) { ((SYM *)exptr->v.p[0]->v.p[0])->inreg = TRUE; ((SYM *)exptr->v.p[0]->v.p[0])->value.i = -csp->reg; } } } csp = csp->next; } }
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: typeargs = method; AST_GET_CHILDREN_NO_DECL(receiver, receiver, method); break; default: {} } // Generate the receiver type. const char* method_name = ast_name(method); ast_t* type = ast_type(receiver); gentype_t g; if(!gentype(c, type, &g)) return NULL; // Generate the arguments. LLVMTypeRef f_type = genfun_sig(c, &g, method_name, typeargs); if(f_type == NULL) { ast_error(ast, "couldn't create a signature for '%s'", method_name); return NULL; } size_t count = ast_childcount(positional) + 1; size_t buf_size = count * sizeof(void*); LLVMValueRef* args = (LLVMValueRef*)ponyint_pool_alloc_size(buf_size); LLVMTypeRef* params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size); LLVMGetParamTypes(f_type, params); ast_t* arg = ast_child(positional); int i = 1; while(arg != NULL) { LLVMValueRef value = make_arg(c, params[i], arg); if(value == NULL) { ponyint_pool_free_size(buf_size, args); ponyint_pool_free_size(buf_size, params); return NULL; } args[i] = value; arg = ast_sibling(arg); i++; } // 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, &g)) { switch(ast_id(postfix)) { case TK_NEWREF: case TK_NEWBEREF: { ast_t* parent = ast_parent(ast); ast_t* sibling = ast_sibling(ast); // If we're constructing an embed field, pass a pointer to the field // as the receiver. Otherwise, allocate an object. if((ast_id(parent) == TK_ASSIGN) && (ast_id(sibling) == TK_EMBEDREF)) args[0] = gen_fieldptr(c, sibling); else args[0] = gencall_alloc(c, &g); break; } case TK_BEREF: case TK_FUNREF: args[0] = gen_expr(c, receiver); break; default: assert(0); return NULL; } } else { // Use a null for the receiver type. args[0] = LLVMConstNull(g.use_type); } // Always emit location info for a call, to prevent inlining errors. This may // be disabled in dispatch_function, if the target function has no debug // info set. ast_setdebug(ast, true); dwarf_location(&c->dwarf, ast); // Static or virtual dispatch. LLVMValueRef func = dispatch_function(c, ast, &g, args[0], method_name, typeargs); LLVMValueRef r = NULL; if(func != NULL) { // If we can error out and we have an invoke target, generate an invoke // instead of a call. 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); } ponyint_pool_free_size(buf_size, args); ponyint_pool_free_size(buf_size, params); return r; }
static LLVMValueRef assign_rvalue(compile_t* c, ast_t* left, ast_t* r_type, LLVMValueRef r_value) { switch(ast_id(left)) { case TK_SEQ: // The actual expression is inside a sequence node. while(ast_id(left) == TK_SEQ) { assert(ast_childcount(left) == 1); left = ast_child(left); } return assign_rvalue(c, left, r_type, r_value); case TK_VAR: case TK_LET: { // Generate the locals. if(gen_localdecl(c, left) == NULL) return NULL; return assign_rvalue(c, ast_child(left), r_type, r_value); } case TK_FVARREF: case TK_FLETREF: { // The result is the previous value of the field. LLVMValueRef l_value = gen_fieldptr(c, left); return assign_one(c, l_value, r_value, r_type); } case TK_EMBEDREF: { // Do nothing. The embed field was already passed as the receiver. return GEN_NOVALUE; } case TK_VARREF: { // The result is the previous value of the local. LLVMValueRef l_value = gen_localptr(c, left); return assign_one(c, l_value, r_value, r_type); } case TK_TUPLE: { // If the l_value is a tuple, assemble it as the result. LLVMValueRef result = gen_expr(c, left); if(result == NULL) return NULL; if(!assign_tuple(c, left, r_type, r_value)) return NULL; // Return the original tuple. return result; } case TK_ID: { // We may have recursed here from a VAR or LET or arrived directly. const char* name = ast_name(left); LLVMValueRef l_value = codegen_getlocal(c, name); return assign_one(c, l_value, r_value, r_type); } default: {} } assert(0); return NULL; }
gen_clause (struct clause_list *clause, int n_args, struct put_fnct *put) { struct expr_list *l, *vars, *var; int i; sput (put, "\t/* clause */\n"); vars = NULL; for (i=0; i<n_args; i++) vars = aj_vars (vars, clause->args[i]); for (l=clause->corps; l!=NULL; l=l->next) vars = aj_vars (vars, l->first); for (var=vars; var!=NULL; var=var->next) { sprintf (buf, "\texpr val_%s, var_%s;\n", var->first->name, var->first->name); sput (put, buf); } sput (put, "\t\talt_process = getpl (k) -> alt;\n"); for (var=vars; var!=NULL; var=var->next) { sprintf (buf, "\t\tdle(val_%s) dle(var_%s)\n", var->first->name, var->first->name); sput (put, buf); } for (var=vars; var!=NULL; var=var->next) { sprintf (buf, "\t\tval_%s=UNDEF; var_%s=mk_var(&val_%s);\n", var->first->name, var->first->name, var->first->name); sput (put, buf); } sput (put, "#ifdef TRACE\n"); for (i=0; i<n_args; i++) { sprintf (buf, "\t\tprintf (\"\\n\\ta%d = \"); print_expr (a%d);\n", i, i); sput (put, buf); } sput (put, "#endif\n"); for (i=0; i<n_args; i++) { sput (put, "\t\tunify (k, "); gen_expr (clause->args[i], put); sprintf (buf, ", a%d);\n", i); sput (put, buf); sput (put, RESET_NX); } for (l=clause->corps; l!=NULL; l=l->next) { if (!strcmp (l->first->name, "c_code")) { sput (put, "\t/* C code inclusion */\n\t"); sput (put, l->first->args[0]->s); sput (put, "\n\t/* End of C code inclusion */\n"); } else { sprintf (buf, "\t\tpl_%s_%d (k", l->first->name, l->first->n); sput (put, buf); for (i=0; i<l->first->n; i++) { sput (put, ", "); gen_expr (l->first->args[i], put); } sput (put, ");\n"); sput (put, RESET_NX); } } for (i=0; i<n_args; i++) { sprintf (buf, "\t\tunify (k, a%d, ", i); sput (put, buf); gen_expr (clause->args[i], put); sput (put, ");\n"); sput (put, RESET_NX); } sput (put, "#ifdef TRACE\n"); for (i=0; i<n_args; i++) { sprintf (buf, "\t\tprintf (\"\\n\\ta%d = \"); print_expr (a%d);\n", i, i); sput (put, buf); } sput (put, "#endif\n"); }
LLVMValueRef gen_match(compile_t* c, ast_t* ast) { bool needed = is_result_needed(ast); ast_t* type = ast_type(ast); AST_GET_CHILDREN(ast, match_expr, cases, else_expr); // We will have no type if all case have control types. LLVMTypeRef phi_type = NULL; if(needed && !is_control_type(type)) { reach_type_t* t_phi = reach_type(c->reach, type); phi_type = t_phi->use_type; } ast_t* match_type = alias(ast_type(match_expr)); LLVMValueRef match_value = gen_expr(c, match_expr); LLVMBasicBlockRef pattern_block = codegen_block(c, "case_pattern"); LLVMBasicBlockRef else_block = codegen_block(c, "match_else"); LLVMBasicBlockRef post_block = NULL; LLVMBasicBlockRef next_block = NULL; // Jump to the first case. LLVMBuildBr(c->builder, pattern_block); LLVMValueRef phi = GEN_NOVALUE; if(!is_control_type(type)) { // Start the post block so that a case can modify the phi node. post_block = codegen_block(c, "match_post"); LLVMPositionBuilderAtEnd(c->builder, post_block); if(needed) phi = LLVMBuildPhi(c->builder, phi_type, ""); else phi = GEN_NOTNEEDED; } // Iterate over the cases. ast_t* the_case = ast_child(cases); while(the_case != NULL) { ast_t* next_case = ast_sibling(the_case); if(next_case != NULL) next_block = codegen_block(c, "case_pattern"); else next_block = else_block; AST_GET_CHILDREN(the_case, pattern, guard, body); LLVMPositionBuilderAtEnd(c->builder, pattern_block); codegen_pushscope(c, the_case); ast_t* pattern_type = ast_type(pattern); bool ok = true; if(is_matchtype(match_type, pattern_type, c->opt) != MATCHTYPE_ACCEPT) { // If there's no possible match, jump directly to the next block. LLVMBuildBr(c->builder, next_block); } else { // Check the pattern. ok = static_match(c, match_value, match_type, pattern, next_block); // Check the guard. ok = ok && guard_match(c, guard, next_block); // Case body. ok = ok && case_body(c, body, post_block, phi, phi_type); } codegen_popscope(c); if(!ok) { ast_free_unattached(match_type); return NULL; } the_case = next_case; pattern_block = next_block; } ast_free_unattached(match_type); // Else body. LLVMPositionBuilderAtEnd(c->builder, else_block); codegen_pushscope(c, else_expr); bool ok = case_body(c, else_expr, post_block, phi, phi_type); codegen_popscope(c); if(!ok) return NULL; if(post_block != NULL) LLVMPositionBuilderAtEnd(c->builder, post_block); return phi; }
void gen_expr_style_assign_compound(expr *e) { gen_expr(e->lhs->lhs); stylef(" %s= ", op_to_str(e->op)); gen_expr(e->rhs); }
LLVMValueRef gen_expr(compile_t* c, ast_t* ast) { LLVMValueRef ret; bool has_scope = ast_has_scope(ast); if(has_scope) codegen_pushscope(c, ast); switch(ast_id(ast)) { case TK_SEQ: ret = gen_seq(c, ast); break; case TK_FVARREF: case TK_FLETREF: ret = gen_fieldload(c, ast); break; case TK_EMBEDREF: ret = gen_fieldptr(c, ast); break; case TK_PARAMREF: ret = gen_param(c, ast); break; case TK_VAR: case TK_LET: case TK_MATCH_CAPTURE: ret = gen_localdecl(c, ast); break; case TK_VARREF: case TK_LETREF: ret = gen_localload(c, ast); break; case TK_IF: ret = gen_if(c, ast); break; case TK_WHILE: ret = gen_while(c, ast); break; case TK_REPEAT: ret = gen_repeat(c, ast); break; case TK_TRY: case TK_TRY_NO_CHECK: ret = gen_try(c, ast); break; case TK_MATCH: ret = gen_match(c, ast); break; case TK_CALL: ret = gen_call(c, ast); break; case TK_CONSUME: ret = gen_expr(c, ast_childidx(ast, 1)); break; case TK_RECOVER: ret = gen_expr(c, ast_childidx(ast, 1)); break; case TK_BREAK: ret = gen_break(c, ast); break; case TK_CONTINUE: ret = gen_continue(c, ast); break; case TK_RETURN: ret = gen_return(c, ast); break; case TK_ERROR: ret = gen_error(c, ast); break; case TK_IS: ret = gen_is(c, ast); break; case TK_ISNT: ret = gen_isnt(c, ast); break; case TK_ASSIGN: ret = gen_assign(c, ast); break; case TK_THIS: ret = gen_this(c, ast); break; case TK_TRUE: ret = LLVMConstInt(c->i1, 1, false); break; case TK_FALSE: ret = LLVMConstInt(c->i1, 0, false); break; case TK_INT: ret = gen_int(c, ast); break; case TK_FLOAT: ret = gen_float(c, ast); break; case TK_STRING: ret = gen_string(c, ast); break; case TK_TUPLE: ret = gen_tuple(c, ast); break; case TK_FFICALL: ret = gen_ffi(c, ast); break; case TK_ADDRESS: ret = gen_addressof(c, ast); break; case TK_DIGESTOF: ret = gen_digestof(c, ast); break; case TK_DONTCARE: ret = GEN_NOVALUE; break; case TK_COMPILE_INTRINSIC: ast_error(c->opt->check.errors, ast, "unimplemented compile intrinsic"); return NULL; case TK_COMPILE_ERROR: { ast_t* reason_seq = ast_child(ast); ast_t* reason = ast_child(reason_seq); ast_error(c->opt->check.errors, ast, "compile error: %s", ast_name(reason)); return NULL; } default: ast_error(c->opt->check.errors, ast, "not implemented (codegen unknown)"); return NULL; } if(has_scope) codegen_popscope(c); return ret; }
void genreturn(STATEMENT *stmt, SYMBOL *funcsp, int flag, int noepilogue, IMODE *allocaAP) /* * generate a return statement. */ { IMODE *ap = NULL, *ap1, *ap3; EXPRESSION ep; int size; /* returns a value? */ if (stmt != 0 && stmt->select != 0) { if (basetype(funcsp->tp)->btp && (isstructured(basetype(funcsp->tp)->btp) || basetype(basetype(funcsp->tp)->btp)->type == bt_memberptr)) { EXPRESSION *en = anonymousVar(sc_parameter, &stdpointer); SYMBOL *sp = en->v.sp; gen_expr(funcsp, stmt->select, 0, ISZ_ADDR); DumpIncDec(funcsp); sp->offset = chosenAssembler->arch->retblocksize; sp->allocate = FALSE; if ((funcsp->linkage == lk_pascal) && basetype(funcsp->tp)->syms->table[0] && ((SYMBOL *)basetype(funcsp->tp)->syms->table[0])->tp->type != bt_void) sp->offset = funcsp->paramsize; deref(&stdpointer, &en); ap = gen_expr(funcsp, en, 0, ISZ_ADDR); size = ISZ_ADDR; } else { size = natural_size(stmt->select); ap3 = gen_expr(funcsp, stmt->select, 0, size); DumpIncDec(funcsp); ap = LookupLoadTemp(NULL, ap3); if (ap != ap3) { IMODE *barrier; if (stmt->select->isatomic) { barrier = doatomicFence(funcsp, stmt->select, NULL); } gen_icode(i_assn, ap, ap3, NULL); if (stmt->select->isatomic) { doatomicFence(funcsp, stmt->select, barrier); } } if (abs(size) < ISZ_UINT) size = -ISZ_UINT; } } else { DumpIncDec(funcsp); } if (ap) { ap1 = tempreg(size, 0); ap1->retval = TRUE; gen_icode(i_assn, ap1, ap, 0); } if (stmt && stmt->destexp) { gen_expr(funcsp, stmt->destexp, F_NOVALUE, ISZ_ADDR); } /* create the return or a branch to the return * return is put at end of function... */ if (flag) { int retsize = 0; if (funcsp->linkage == lk_pascal || funcsp->linkage == lk_stdcall) { retsize = funcsp->paramsize ; } gen_label(retlab); if (!noepilogue) { if (allocaAP) { gen_icode(i_loadstack, 0, allocaAP, 0); } /* if (funcsp->loadds && funcsp->farproc) gen_icode(i_unloadcontext,0,0,0); */ if (cparams.prm_xcept && funcsp->xc && funcsp->xc->xcRundownFunc) gen_expr(funcsp, funcsp->xc->xcRundownFunc, F_NOVALUE, ISZ_UINT); gen_icode(i_epilogue,0,0,0); if (funcsp->linkage == lk_interrupt || funcsp->linkage == lk_fault) { /* if (funcsp->loadds) gen_icode(i_unloadcontext,0,0,0); */ gen_icode(i_popcontext, 0,0,0); gen_icode(i_rett, 0, make_immed(ISZ_UINT,funcsp->linkage == lk_interrupt), 0); } else { gen_icode(i_ret, 0, make_immed(ISZ_UINT,retsize), 0); } } } else { /* not using gen_igoto because it will make a new block */ gen_icode(i_goto, NULL, NULL, NULL); intermed_tail->dc.v.label = retlab; } }
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_ffi(compile_t* c, ast_t* ast) { AST_GET_CHILDREN(ast, id, typeargs, args); // Get the function name, +1 to skip leading @ const char* f_name = ast_name(id) + 1; // Generate the return type. ast_t* type = ast_type(ast); gentype_t g; // Emit dwarf location of ffi call dwarf_location(&c->dwarf, ast); if(!gentype(c, type, &g)) return NULL; // Get the function. LLVMValueRef func = LLVMGetNamedFunction(c->module, f_name); if(func == NULL) { // If we have no prototype, declare one. if(!strncmp(f_name, "llvm.", 5)) { // Intrinsic, so use the exact types we supply. int count = (int)ast_childcount(args); size_t buf_size = count * sizeof(LLVMTypeRef); LLVMTypeRef* f_params = (LLVMTypeRef*)pool_alloc_size(buf_size); count = 0; ast_t* arg = ast_child(args); while(arg != NULL) { ast_t* p_type = ast_type(arg); gentype_t param_g; if(!gentype(c, p_type, ¶m_g)) return NULL; f_params[count++] = param_g.use_type; arg = ast_sibling(arg); } // We may have generated the function by generating a parameter type. func = LLVMGetNamedFunction(c->module, f_name); if(func == NULL) { LLVMTypeRef r_type; if(g.underlying == TK_TUPLETYPE) { // Can't use the named type. Build an unnamed type with the same // elements. unsigned int count = LLVMCountStructElementTypes(g.use_type); size_t buf_size = count * sizeof(LLVMTypeRef); LLVMTypeRef* e_types = (LLVMTypeRef*)pool_alloc_size(buf_size); LLVMGetStructElementTypes(g.use_type, e_types); r_type = LLVMStructTypeInContext(c->context, e_types, count, false); pool_free_size(buf_size, e_types); } else { r_type = g.use_type; } LLVMTypeRef f_type = LLVMFunctionType(r_type, f_params, count, false); func = LLVMAddFunction(c->module, f_name, f_type); if(!ast_canerror(ast)) LLVMAddFunctionAttr(func, LLVMNoUnwindAttribute); } pool_free_size(buf_size, f_params); } else { // Make it varargs. LLVMTypeRef f_type = LLVMFunctionType(g.use_type, NULL, 0, true); func = LLVMAddFunction(c->module, f_name, f_type); if(!ast_canerror(ast)) LLVMAddFunctionAttr(func, LLVMNoUnwindAttribute); } } // Generate the arguments. int count = (int)ast_childcount(args); size_t buf_size = count * sizeof(LLVMValueRef); LLVMValueRef* f_args = (LLVMValueRef*)pool_alloc_size(buf_size); ast_t* arg = ast_child(args); for(int i = 0; i < count; i++) { f_args[i] = gen_expr(c, arg); if(f_args[i] == NULL) { 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; if(ast_canerror(ast) && (c->frame->invoke_target != NULL)) result = invoke_fun(c, func, f_args, count, "", false); else result = LLVMBuildCall(c->builder, func, f_args, count, ""); pool_free_size(buf_size, f_args); // Special case a None return value, which is used for void functions. if(is_none(type)) return g.instance; return result; }