static void trace_dynamic(compile_t* c, LLVMValueRef ctx, LLVMValueRef object, ast_t* type, ast_t* orig, ast_t* tuple, LLVMBasicBlockRef next_block) { switch(ast_id(type)) { case TK_UNIONTYPE: case TK_ISECTTYPE: trace_dynamic_union_or_isect(c, ctx, object, type, orig, tuple, next_block); break; case TK_TUPLETYPE: { // This is a boxed tuple. Trace the box, then handle the elements. trace_tag(c, ctx, object); LLVMValueRef desc = gendesc_fetch(c, object); LLVMValueRef ptr = gendesc_ptr_to_fields(c, object, desc); trace_dynamic_tuple(c, ctx, ptr, desc, type, orig, tuple); break; } case TK_NOMINAL: trace_dynamic_nominal(c, ctx, object, type, orig, tuple, next_block); break; default: {} } }
static bool static_tuple(compile_t* c, LLVMValueRef value, ast_t* type, ast_t* pattern, LLVMBasicBlockRef next_block) { switch(ast_id(type)) { case TK_UNIONTYPE: case TK_ISECTTYPE: { // Read the dynamic type and get a base pointer. LLVMValueRef desc = gendesc_fetch(c, value); LLVMValueRef ptr = gendesc_ptr_to_fields(c, value, desc); return dynamic_tuple_ptr(c, ptr, desc, pattern, next_block); } case TK_TUPLETYPE: return static_tuple_from_tuple(c, value, type, pattern, next_block); case TK_ARROW: return static_tuple(c, value, ast_childidx(type, 1), pattern, next_block); default: {} } // Can't match. LLVMBuildBr(c->builder, next_block); return true; }
static LLVMValueRef raw_is_box(compile_t* c, ast_t* left_type, LLVMValueRef l_value, LLVMValueRef r_value) { pony_assert(LLVMGetTypeKind(LLVMTypeOf(r_value)) == LLVMPointerTypeKind); LLVMValueRef r_desc = gendesc_fetch(c, r_value); LLVMValueRef same_type = gendesc_isentity(c, r_desc, left_type); pony_assert(same_type != GEN_NOVALUE); LLVMBasicBlockRef this_block = LLVMGetInsertBlock(c->builder); LLVMBasicBlockRef value_block = codegen_block(c, "is_value"); LLVMBasicBlockRef post_block = codegen_block(c, "is_post"); LLVMBuildCondBr(c->builder, same_type, value_block, post_block); LLVMPositionBuilderAtEnd(c->builder, value_block); r_value = gen_unbox(c, left_type, r_value); LLVMValueRef is_value = gen_is_value(c, left_type, left_type, l_value, r_value); LLVMBuildBr(c->builder, post_block); value_block = LLVMGetInsertBlock(c->builder); LLVMPositionBuilderAtEnd(c->builder, post_block); LLVMValueRef phi = LLVMBuildPhi(c->builder, c->i1, ""); LLVMValueRef zero = LLVMConstInt(c->i1, 0, false); LLVMAddIncoming(phi, &is_value, &value_block, 1); LLVMAddIncoming(phi, &zero, &this_block, 1); return phi; }
static void trace_dynamic_nominal(compile_t* c, LLVMValueRef ctx, LLVMValueRef object, ast_t* type, ast_t* orig, ast_t* tuple, LLVMBasicBlockRef next_block) { // Skip if a primitive. ast_t* def = (ast_t*)ast_data(type); if(ast_id(def) == TK_PRIMITIVE) return; // If it's not possible to use match or as to extract this type from the // original type, there's no need to trace as this type. if(tuple != NULL) { // We are a tuple element. Our type is in the correct position in the // tuple, everything else is TK_DONTCARE. if(is_matchtype(orig, tuple) != MATCHTYPE_ACCEPT) return; } else { // We aren't a tuple element. if(is_matchtype(orig, type) != MATCHTYPE_ACCEPT) return; } // We aren't always this type. We need to check dynamically. LLVMValueRef desc = gendesc_fetch(c, object); LLVMValueRef test = gendesc_isnominal(c, desc, type); LLVMBasicBlockRef is_true = codegen_block(c, ""); LLVMBasicBlockRef is_false = codegen_block(c, ""); LLVMBuildCondBr(c->builder, test, is_true, is_false); // Trace as this type. LLVMPositionBuilderAtEnd(c->builder, is_true); gentrace(c, ctx, object, type); // If we have traced as known, unknown or actor, we're done with this // element. Otherwise, continue tracing this as if the match had been // unsuccessful. switch(trace_type(type)) { case TRACE_KNOWN: case TRACE_UNKNOWN: case TRACE_KNOWN_VAL: case TRACE_UNKNOWN_VAL: case TRACE_ACTOR: LLVMBuildBr(c->builder, next_block); break; default: LLVMBuildBr(c->builder, is_false); break; } // Carry on, whether we have traced or not. LLVMPositionBuilderAtEnd(c->builder, is_false); }
static LLVMValueRef gen_digestof_box(compile_t* c, reach_type_t* type, LLVMValueRef value, int boxed_subtype) { pony_assert(LLVMGetTypeKind(LLVMTypeOf(value)) == LLVMPointerTypeKind); LLVMBasicBlockRef box_block = NULL; LLVMBasicBlockRef nonbox_block = NULL; LLVMBasicBlockRef post_block = NULL; LLVMValueRef desc = gendesc_fetch(c, value); if((boxed_subtype & SUBTYPE_KIND_UNBOXED) != 0) { box_block = codegen_block(c, "digestof_box"); nonbox_block = codegen_block(c, "digestof_nonbox"); post_block = codegen_block(c, "digestof_post"); // Check if it's a boxed value. LLVMValueRef type_id = gendesc_typeid(c, desc); LLVMValueRef boxed_mask = LLVMConstInt(c->i32, 1, false); LLVMValueRef is_boxed = LLVMBuildAnd(c->builder, type_id, boxed_mask, ""); LLVMValueRef zero = LLVMConstInt(c->i32, 0, false); is_boxed = LLVMBuildICmp(c->builder, LLVMIntEQ, is_boxed, zero, ""); LLVMBuildCondBr(c->builder, is_boxed, box_block, nonbox_block); LLVMPositionBuilderAtEnd(c->builder, box_block); } // Call the type-specific __digestof function, which will unbox the value. reach_method_t* digest_fn = reach_method(type, TK_BOX, stringtab("__digestof"), NULL); pony_assert(digest_fn != NULL); LLVMValueRef func = gendesc_vtable(c, desc, digest_fn->vtable_index); LLVMTypeRef fn_type = LLVMFunctionType(c->intptr, &c->object_ptr, 1, false); func = LLVMBuildBitCast(c->builder, func, LLVMPointerType(fn_type, 0), ""); LLVMValueRef box_digest = codegen_call(c, func, &value, 1, true); if((boxed_subtype & SUBTYPE_KIND_UNBOXED) != 0) { LLVMBuildBr(c->builder, post_block); // Just cast the address. LLVMPositionBuilderAtEnd(c->builder, nonbox_block); LLVMValueRef nonbox_digest = LLVMBuildPtrToInt(c->builder, value, c->intptr, ""); LLVMBuildBr(c->builder, post_block); LLVMPositionBuilderAtEnd(c->builder, post_block); LLVMValueRef phi = LLVMBuildPhi(c->builder, c->intptr, ""); LLVMAddIncoming(phi, &box_digest, &box_block, 1); LLVMAddIncoming(phi, &nonbox_digest, &nonbox_block, 1); return phi; } else { return box_digest; } }
LLVMValueRef gendesc_vtable(compile_t* c, LLVMValueRef object, size_t colour) { LLVMValueRef desc = gendesc_fetch(c, object); LLVMValueRef vtable = LLVMBuildStructGEP(c->builder, desc, DESC_VTABLE, ""); LLVMValueRef gep[2]; gep[0] = LLVMConstInt(c->i32, 0, false); gep[1] = LLVMConstInt(c->i32, colour, false); LLVMValueRef func_ptr = LLVMBuildInBoundsGEP(c->builder, vtable, gep, 2, ""); return LLVMBuildLoad(c->builder, func_ptr, ""); }
static bool check_tuple(compile_t* c, LLVMValueRef ptr, LLVMValueRef desc, ast_t* pattern_type, LLVMBasicBlockRef next_block) { // First check cardinality. size_t size = ast_childcount(pattern_type); check_cardinality(c, desc, size, next_block); // If we get here, the match expression has the right cardinality. ast_t* pattern_child = ast_child(pattern_type); for(int i = 0; pattern_child != NULL; i++) { // Get the field offset and field descriptor from the tuple descriptor. LLVMValueRef field_info = gendesc_fieldinfo(c, desc, i); LLVMValueRef field_ptr = gendesc_fieldptr(c, ptr, field_info); LLVMValueRef field_desc = gendesc_fielddesc(c, field_info); // If we have a null descriptor, load the object. LLVMBasicBlockRef null_block = codegen_block(c, "null_desc"); LLVMBasicBlockRef nonnull_block = codegen_block(c, "nonnull_desc"); LLVMBasicBlockRef continue_block = codegen_block(c, "merge_desc"); LLVMValueRef test = LLVMBuildIsNull(c->builder, field_desc, ""); LLVMBuildCondBr(c->builder, test, null_block, nonnull_block); // Load the object, load its descriptor, and continue from there. LLVMPositionBuilderAtEnd(c->builder, null_block); LLVMTypeRef ptr_type = LLVMPointerType(c->object_ptr, 0); LLVMValueRef object_ptr = LLVMBuildIntToPtr(c->builder, field_ptr, ptr_type, ""); LLVMValueRef object = LLVMBuildLoad(c->builder, object_ptr, ""); LLVMValueRef object_desc = gendesc_fetch(c, object); object_ptr = gendesc_ptr_to_fields(c, object, object_desc); if(!check_type(c, object_ptr, object_desc, pattern_child, next_block)) return false; LLVMBuildBr(c->builder, continue_block); // Continue with the pointer and descriptor. LLVMPositionBuilderAtEnd(c->builder, nonnull_block); if(!check_type(c, field_ptr, field_desc, pattern_child, next_block)) return false; LLVMBuildBr(c->builder, continue_block); // Merge the two branches. LLVMPositionBuilderAtEnd(c->builder, continue_block); pattern_child = ast_sibling(pattern_child); } return true; }
static bool dynamic_tuple_element(compile_t* c, LLVMValueRef ptr, LLVMValueRef desc, ast_t* pattern, LLVMBasicBlockRef next_block, int elem) { // If we have a capture, generate the alloca now. switch(ast_id(pattern)) { case TK_MATCH_CAPTURE: if(gen_localdecl(c, pattern) == NULL) return false; break; default: {} } // Get the field offset and field descriptor from the tuple descriptor. LLVMValueRef field_info = gendesc_fieldinfo(c, desc, elem); LLVMValueRef field_ptr = gendesc_fieldptr(c, ptr, field_info); LLVMValueRef field_desc = gendesc_fielddesc(c, field_info); // If we have a null descriptor, load the object. LLVMBasicBlockRef null_block = codegen_block(c, "null_desc"); LLVMBasicBlockRef nonnull_block = codegen_block(c, "nonnull_desc"); LLVMBasicBlockRef continue_block = codegen_block(c, "merge_desc"); LLVMValueRef test = LLVMBuildIsNull(c->builder, field_desc, ""); LLVMBuildCondBr(c->builder, test, null_block, nonnull_block); // Load the object, load its descriptor, and continue from there. LLVMPositionBuilderAtEnd(c->builder, null_block); LLVMTypeRef ptr_type = LLVMPointerType(c->object_ptr, 0); LLVMValueRef object_ptr = LLVMBuildIntToPtr(c->builder, field_ptr, ptr_type, ""); LLVMValueRef object = LLVMBuildLoad(c->builder, object_ptr, ""); LLVMValueRef object_desc = gendesc_fetch(c, object); if(!dynamic_match_object(c, object, object_desc, pattern, next_block)) return false; LLVMBuildBr(c->builder, continue_block); // Continue with the pointer and descriptor. LLVMPositionBuilderAtEnd(c->builder, nonnull_block); if(!dynamic_match_ptr(c, field_ptr, field_desc, pattern, next_block)) return false; LLVMBuildBr(c->builder, continue_block); // Merge the two branches. LLVMPositionBuilderAtEnd(c->builder, continue_block); return true; }
static bool static_value(compile_t* c, LLVMValueRef value, ast_t* type, ast_t* pattern, LLVMBasicBlockRef next_block) { // Get the type of the right-hand side of the pattern's eq() function. ast_t* param_type = eq_param_type(c, pattern); if(!is_subtype(type, param_type, NULL, c->opt)) { // Switch to dynamic value checking. assert(LLVMTypeOf(value) == c->object_ptr); LLVMValueRef desc = gendesc_fetch(c, value); return dynamic_value_object(c, value, desc, pattern, next_block); } return check_value(c, pattern, param_type, value, next_block); }
static bool static_capture(compile_t* c, LLVMValueRef value, ast_t* type, ast_t* pattern, LLVMBasicBlockRef next_block) { // The pattern is a capture. Make sure we are the right type, then assign. ast_t* pattern_type = ast_type(pattern); // Generate the alloca if(gen_localdecl(c, pattern) == NULL) return false; if(!is_subtype(type, pattern_type, NULL, c->opt)) { // Switch to dynamic capture. assert(LLVMTypeOf(value) == c->object_ptr); LLVMValueRef desc = gendesc_fetch(c, value); return dynamic_capture_object(c, value, desc, pattern, next_block); } return gen_assign_value(c, pattern, value, type) != NULL; }
static LLVMValueRef dispatch_function(compile_t* c, reach_type_t* t, reach_method_t* m, LLVMValueRef l_value) { compile_method_t* c_m = (compile_method_t*)m->c_method; if(t->bare_method == m) return LLVMBuildBitCast(c->builder, l_value, LLVMPointerType(c_m->func_type, 0), ""); switch(t->underlying) { case TK_UNIONTYPE: case TK_ISECTTYPE: case TK_INTERFACE: case TK_TRAIT: { pony_assert(t->bare_method == NULL); // Get the function from the vtable. LLVMValueRef func = gendesc_vtable(c, gendesc_fetch(c, l_value), m->vtable_index); return LLVMBuildBitCast(c->builder, func, LLVMPointerType(c_m->func_type, 0), ""); } case TK_PRIMITIVE: case TK_STRUCT: case TK_CLASS: case TK_ACTOR: { // Static, get the actual function. return c_m->func; } default: {} } pony_assert(0); return NULL; }
LLVMValueRef gendesc_dispatch(compile_t* c, LLVMValueRef object) { return desc_field(c, gendesc_fetch(c, object), DESC_DISPATCH); }
LLVMValueRef gendesc_trace(compile_t* c, LLVMValueRef object) { return desc_field(c, gendesc_fetch(c, object), DESC_TRACE); }
static void trace_dynamic_nominal(compile_t* c, LLVMValueRef ctx, LLVMValueRef object, ast_t* type, ast_t* orig, ast_t* tuple, LLVMBasicBlockRef next_block) { pony_assert(ast_id(type) == TK_NOMINAL); // Skip if a primitive. ast_t* def = (ast_t*)ast_data(type); if(ast_id(def) == TK_PRIMITIVE) return; int mutability = trace_cap_nominal(c->opt, type, orig, tuple); // If we can't extract the element from the original type, there is no need to // trace the element. if(mutability == -1) return; token_id dst_cap = TK_TAG; switch(mutability) { case PONY_TRACE_MUTABLE: dst_cap = TK_ISO; break; case PONY_TRACE_IMMUTABLE: dst_cap = TK_VAL; break; default: {} } ast_t* dst_type = ast_dup(type); ast_t* dst_cap_ast = cap_fetch(dst_type); ast_setid(dst_cap_ast, dst_cap); ast_t* dst_eph = ast_sibling(dst_cap_ast); if(ast_id(dst_eph) == TK_EPHEMERAL) ast_setid(dst_eph, TK_NONE); // We aren't always this type. We need to check dynamically. LLVMValueRef desc = gendesc_fetch(c, object); LLVMValueRef test = gendesc_isnominal(c, desc, type); LLVMBasicBlockRef is_true = codegen_block(c, ""); LLVMBasicBlockRef is_false = codegen_block(c, ""); LLVMBuildCondBr(c->builder, test, is_true, is_false); // Trace as this type. LLVMPositionBuilderAtEnd(c->builder, is_true); gentrace(c, ctx, object, object, type, dst_type); ast_free_unattached(dst_type); // If we have traced as mut or val, we're done with this element. Otherwise, // continue tracing this as if the match had been unsuccessful. if(mutability != PONY_TRACE_OPAQUE) LLVMBuildBr(c->builder, next_block); else LLVMBuildBr(c->builder, is_false); // Carry on, whether we have traced or not. LLVMPositionBuilderAtEnd(c->builder, is_false); }
static LLVMValueRef box_is_box(compile_t* c, ast_t* left_type, LLVMValueRef l_value, LLVMValueRef r_value, int possible_boxes) { pony_assert(LLVMGetTypeKind(LLVMTypeOf(l_value)) == LLVMPointerTypeKind); pony_assert(LLVMGetTypeKind(LLVMTypeOf(r_value)) == LLVMPointerTypeKind); LLVMBasicBlockRef this_block = LLVMGetInsertBlock(c->builder); LLVMBasicBlockRef checkbox_block = codegen_block(c, "is_checkbox"); LLVMBasicBlockRef box_block = codegen_block(c, "is_box"); LLVMBasicBlockRef num_block = NULL; if((possible_boxes & BOXED_SUBTYPES_NUMERIC) != 0) num_block = codegen_block(c, "is_num"); LLVMBasicBlockRef tuple_block = NULL; if((possible_boxes & BOXED_SUBTYPES_TUPLE) != 0) tuple_block = codegen_block(c, "is_tuple"); LLVMBasicBlockRef post_block = codegen_block(c, "is_post"); LLVMValueRef eq_addr = LLVMBuildICmp(c->builder, LLVMIntEQ, l_value, r_value, ""); LLVMBuildCondBr(c->builder, eq_addr, post_block, checkbox_block); // Check whether we have two boxed objects of the same type. LLVMPositionBuilderAtEnd(c->builder, checkbox_block); LLVMValueRef l_desc = gendesc_fetch(c, l_value); LLVMValueRef r_desc = gendesc_fetch(c, r_value); LLVMValueRef same_type = LLVMBuildICmp(c->builder, LLVMIntEQ, l_desc, r_desc, ""); LLVMValueRef l_typeid = NULL; if((possible_boxes & BOXED_SUBTYPES_UNBOXED) != 0) { l_typeid = gendesc_typeid(c, l_value); LLVMValueRef boxed_mask = LLVMConstInt(c->i32, 1, false); LLVMValueRef left_boxed = LLVMBuildAnd(c->builder, l_typeid, boxed_mask, ""); LLVMValueRef zero = LLVMConstInt(c->i32, 0, false); left_boxed = LLVMBuildICmp(c->builder, LLVMIntEQ, left_boxed, zero, ""); LLVMValueRef both_boxed = LLVMBuildAnd(c->builder, same_type, left_boxed, ""); LLVMBuildCondBr(c->builder, both_boxed, box_block, post_block); } else { LLVMBuildCondBr(c->builder, same_type, box_block, post_block); } // Check whether it's a numeric primitive or a tuple. LLVMPositionBuilderAtEnd(c->builder, box_block); if((possible_boxes & BOXED_SUBTYPES_BOXED) == BOXED_SUBTYPES_BOXED) { if(l_typeid == NULL) l_typeid = gendesc_typeid(c, l_value); LLVMValueRef num_mask = LLVMConstInt(c->i32, 2, false); LLVMValueRef boxed_num = LLVMBuildAnd(c->builder, l_typeid, num_mask, ""); LLVMValueRef zero = LLVMConstInt(c->i32, 0, false); boxed_num = LLVMBuildICmp(c->builder, LLVMIntEQ, boxed_num, zero, ""); LLVMBuildCondBr(c->builder, boxed_num, num_block, tuple_block); } else if((possible_boxes & BOXED_SUBTYPES_NUMERIC) != 0) { LLVMBuildBr(c->builder, num_block); } else { pony_assert((possible_boxes & BOXED_SUBTYPES_TUPLE) != 0); LLVMBuildBr(c->builder, tuple_block); } LLVMValueRef args[3]; LLVMValueRef is_num = NULL; if(num_block != NULL) { // Get the machine word size and memcmp without unboxing. LLVMPositionBuilderAtEnd(c->builder, num_block); if(l_typeid == NULL) l_typeid = gendesc_typeid(c, l_value); LLVMValueRef num_sizes = LLVMBuildBitCast(c->builder, c->numeric_sizes, c->void_ptr, ""); args[0] = LLVMBuildZExt(c->builder, l_typeid, c->intptr, ""); LLVMValueRef size = LLVMBuildInBoundsGEP(c->builder, num_sizes, args, 1, ""); size = LLVMBuildBitCast(c->builder, size, LLVMPointerType(c->i32, 0), ""); size = LLVMBuildLoad(c->builder, size, ""); LLVMSetAlignment(size, 4); LLVMValueRef one = LLVMConstInt(c->i32, 1, false); args[0] = LLVMBuildInBoundsGEP(c->builder, l_value, &one, 1, ""); args[0] = LLVMBuildBitCast(c->builder, args[0], c->void_ptr, ""); args[1] = LLVMBuildInBoundsGEP(c->builder, r_value, &one, 1, ""); args[1] = LLVMBuildBitCast(c->builder, args[1], c->void_ptr, ""); args[2] = LLVMBuildZExt(c->builder, size, c->intptr, ""); is_num = gencall_runtime(c, "memcmp", args, 3, ""); is_num = LLVMBuildICmp(c->builder, LLVMIntEQ, is_num, LLVMConstInt(c->i32, 0, false), ""); LLVMBuildBr(c->builder, post_block); } LLVMValueRef is_tuple = NULL; if(tuple_block != NULL) { // Call the type-specific __is function, which will unbox the tuples. LLVMPositionBuilderAtEnd(c->builder, tuple_block); reach_type_t* r_left = reach_type(c->reach, left_type); reach_method_t* is_fn = reach_method(r_left, TK_BOX, stringtab("__is"), NULL); pony_assert(is_fn != NULL); LLVMValueRef func = gendesc_vtable(c, l_value, is_fn->vtable_index); LLVMTypeRef params[2]; params[0] = c->object_ptr; params[1] = c->object_ptr; LLVMTypeRef type = LLVMFunctionType(c->i1, params, 2, false); func = LLVMBuildBitCast(c->builder, func, LLVMPointerType(type, 0), ""); args[0] = l_value; args[1] = r_value; is_tuple = codegen_call(c, func, args, 2); LLVMBuildBr(c->builder, post_block); } LLVMPositionBuilderAtEnd(c->builder, post_block); LLVMValueRef phi = LLVMBuildPhi(c->builder, c->i1, ""); LLVMValueRef one = LLVMConstInt(c->i1, 1, false); LLVMValueRef zero = LLVMConstInt(c->i1, 0, false); LLVMAddIncoming(phi, &one, &this_block, 1); if(is_num != NULL) LLVMAddIncoming(phi, &is_num, &num_block, 1); if(is_tuple != NULL) LLVMAddIncoming(phi, &is_tuple, &tuple_block, 1); LLVMAddIncoming(phi, &zero, &checkbox_block, 1); return phi; }