static bool dynamic_capture_ptr(compile_t* c, LLVMValueRef ptr, LLVMValueRef desc, ast_t* pattern, LLVMBasicBlockRef next_block) { // Here, ptr is a pointer to a tuple field. It could be a primitive, an // object, or a nested tuple. ast_t* pattern_type = ast_type(pattern); // Check the runtime type. We pass a pointer to the fields because we may // still need to match a tuple type inside a type expression. if(!check_type(c, ptr, desc, pattern_type, next_block)) return false; // We now know that ptr points to something of type pattern_type, and that // it isn't a boxed primitive or tuple, as that would go through the other // path, ie dynamic_match_object(). We also know it isn't an unboxed tuple. // We can load from ptr with a type based on the static type of the pattern. gentype_t g; if(!gentype(c, pattern_type, &g)) return false; LLVMTypeRef ptr_type = LLVMPointerType(g.use_type, 0); ptr = LLVMBuildIntToPtr(c->builder, ptr, ptr_type, ""); LLVMValueRef value = LLVMBuildLoad(c->builder, ptr, ""); return gen_assign_value(c, pattern, value, pattern_type) != NULL; }
static bool dynamic_value_ptr(compile_t* c, LLVMValueRef ptr, LLVMValueRef desc, 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(pattern); // Check the runtime type. We pass a pointer to the fields because we may // still need to match a tuple type inside a type expression. if(!check_type(c, ptr, desc, param_type, next_block)) return false; // We now know that ptr points to something of type pattern_type, and that // it isn't a boxed primitive, as that would go through the other path, ie // dynamic_match_object(). We also know it isn't an unboxed tuple. We can // load from ptr with a type based on the static type of the pattern. gentype_t g; if(!gentype(c, param_type, &g)) return false; LLVMTypeRef ptr_type = LLVMPointerType(g.use_type, 0); ptr = LLVMBuildIntToPtr(c->builder, ptr, ptr_type, ""); LLVMValueRef value = LLVMBuildLoad(c->builder, ptr, ""); return check_value(c, pattern, param_type, value, next_block); }
static void pointer_offset(compile_t* c, reach_type_t* t, reach_type_t* t_elem) { FIND_METHOD("_offset"); LLVMTypeRef params[3]; params[0] = t->use_type; params[1] = c->intptr; start_function(c, m, t->use_type, params, 2); // Set up a constant integer for the allocation size. size_t size = (size_t)LLVMABISizeOfType(c->target_data, t_elem->use_type); LLVMValueRef l_size = LLVMConstInt(c->intptr, size, false); LLVMValueRef ptr = LLVMGetParam(m->func, 0); LLVMValueRef n = LLVMGetParam(m->func, 1); // Return ptr + (n * sizeof(len)). LLVMValueRef src = LLVMBuildPtrToInt(c->builder, ptr, c->intptr, ""); LLVMValueRef offset = LLVMBuildMul(c->builder, n, l_size, ""); LLVMValueRef result = LLVMBuildAdd(c->builder, src, offset, ""); result = LLVMBuildIntToPtr(c->builder, result, t->use_type, ""); LLVMBuildRet(c->builder, result); codegen_finishfun(c); BOX_FUNCTION(); }
LLVMValueRef gendesc_fieldload(compile_t* c, LLVMValueRef ptr, LLVMValueRef field_info) { LLVMValueRef field_ptr = gendesc_fieldptr(c, ptr, field_info); LLVMValueRef object_ptr = LLVMBuildIntToPtr(c->builder, field_ptr, LLVMPointerType(c->object_ptr, 0), ""); return LLVMBuildLoad(c->builder, object_ptr, ""); }
static void pointer_delete(compile_t* c, reach_type_t* t, reach_type_t* t_elem) { FIND_METHOD("_delete"); LLVMTypeRef params[3]; params[0] = t->use_type; params[1] = c->intptr; params[2] = c->intptr; start_function(c, m, t_elem->use_type, params, 3); // Set up a constant integer for the allocation size. size_t size = (size_t)LLVMABISizeOfType(c->target_data, t_elem->use_type); LLVMValueRef l_size = LLVMConstInt(c->intptr, size, false); LLVMValueRef ptr = LLVMGetParam(m->func, 0); LLVMValueRef n = LLVMGetParam(m->func, 1); LLVMValueRef len = LLVMGetParam(m->func, 2); LLVMValueRef elem_ptr = LLVMBuildBitCast(c->builder, ptr, LLVMPointerType(t_elem->use_type, 0), ""); LLVMValueRef result = LLVMBuildLoad(c->builder, elem_ptr, ""); LLVMValueRef dst = LLVMBuildPtrToInt(c->builder, elem_ptr, c->intptr, ""); LLVMValueRef offset = LLVMBuildMul(c->builder, n, l_size, ""); LLVMValueRef src = LLVMBuildAdd(c->builder, dst, offset, ""); LLVMValueRef elen = LLVMBuildMul(c->builder, len, l_size, ""); LLVMValueRef args[5]; args[0] = LLVMBuildIntToPtr(c->builder, dst, c->void_ptr, ""); args[1] = LLVMBuildIntToPtr(c->builder, src, c->void_ptr, ""); args[2] = elen; args[3] = LLVMConstInt(c->i32, 1, false); args[4] = LLVMConstInt(c->i1, 0, false); // llvm.memmove.*(ptr, ptr + (n * sizeof(elem)), len * sizeof(elem)) if(target_is_ilp32(c->opt->triple)) { gencall_runtime(c, "llvm.memmove.p0i8.p0i8.i32", args, 5, ""); } else { gencall_runtime(c, "llvm.memmove.p0i8.p0i8.i64", args, 5, ""); } // Return ptr[0]. LLVMBuildRet(c->builder, result); codegen_finishfun(c); }
static LLVMValueRef field_loc(compile_t* c, LLVMValueRef offset, LLVMTypeRef structure, LLVMTypeRef ftype, int index) { LLVMValueRef f_offset = LLVMBuildAdd(c->builder, offset, LLVMConstInt(c->intptr, LLVMOffsetOfElement(c->target_data, structure, index), false), ""); return LLVMBuildIntToPtr(c->builder, f_offset, LLVMPointerType(ftype, 0), ""); }
LLVMValueRef build_utcb_get(struct llvm_ctx *ctx) { LLVMTypeRef fntype = LLVMFunctionType(ctx->wordt, NULL, 0, 0); LLVMValueRef func = LLVMConstInlineAsm(fntype, "movl %gs:0,$0\n", "=r,~{dirflag},~{fpsr},~{flags}", 0, 0); LLVMValueRef call = LLVMBuildCall(ctx->builder, func, NULL, 0, "utcbget"); LLVMSetTailCall(call, 1); return LLVMBuildIntToPtr(ctx->builder, call, LLVMPointerType(ctx->wordt, 0), "utcb.wordp"); }
SCM llvm_build_store(SCM scm_function, SCM scm_type, SCM scm_value, SCM scm_address) { struct llvm_function_t *function = get_llvm_function(scm_function); struct llvm_value_t *value = get_llvm_value(scm_value); struct llvm_value_t *address = get_llvm_value(scm_address); int type = scm_to_int(scm_type); LLVMValueRef pointer = LLVMBuildIntToPtr(function->builder, address->value, LLVMPointerType(llvm_type(type), 0), "x"); LLVMBuildStore(function->builder, value->value, pointer); return SCM_UNSPECIFIED; }
SCM llvm_build_load(SCM scm_function, SCM scm_type, SCM scm_address) { SCM retval; struct llvm_function_t *function = get_llvm_function(scm_function); struct llvm_value_t *result = (struct llvm_value_t *)scm_gc_calloc(sizeof(struct llvm_value_t), "llvm value"); SCM_NEWSMOB(retval, llvm_value_tag, result); struct llvm_value_t *address = get_llvm_value(scm_address); int type = scm_to_int(scm_type); LLVMValueRef pointer = LLVMBuildIntToPtr(function->builder, address->value, LLVMPointerType(llvm_type(type), 0), "x"); result->value = LLVMBuildLoad(function->builder, pointer, "x"); return retval; }
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 LLVMValueRef llvm_load_const_buffer( struct lp_build_tgsi_context * bld_base, LLVMValueRef OffsetValue, unsigned ConstantAddressSpace) { LLVMValueRef offset[2] = { LLVMConstInt(LLVMInt64TypeInContext(bld_base->base.gallivm->context), 0, false), OffsetValue }; LLVMTypeRef const_ptr_type = LLVMPointerType(LLVMArrayType(LLVMVectorType(bld_base->base.elem_type, 4), 1024), ConstantAddressSpace); LLVMValueRef const_ptr = LLVMBuildIntToPtr(bld_base->base.gallivm->builder, lp_build_const_int32(bld_base->base.gallivm, 0), const_ptr_type, ""); LLVMValueRef ptr = LLVMBuildGEP(bld_base->base.gallivm->builder, const_ptr, offset, 2, ""); return LLVMBuildLoad(bld_base->base.gallivm->builder, ptr, ""); }
static LLVMValueRef cast_ffi_arg(compile_t* c, ffi_decl_t* decl, ast_t* ast, LLVMValueRef arg, LLVMTypeRef param, const char* name) { if(arg == NULL) return NULL; LLVMTypeRef arg_type = LLVMTypeOf(arg); if(param == arg_type) return arg; if((LLVMABISizeOfType(c->target_data, param) != LLVMABISizeOfType(c->target_data, arg_type))) { report_ffi_type_err(c, decl, ast, name); return NULL; } switch(LLVMGetTypeKind(param)) { case LLVMPointerTypeKind: if(LLVMGetTypeKind(arg_type) == LLVMIntegerTypeKind) return LLVMBuildIntToPtr(c->builder, arg, param, ""); else return LLVMBuildBitCast(c->builder, arg, param, ""); case LLVMIntegerTypeKind: if(LLVMGetTypeKind(arg_type) == LLVMPointerTypeKind) return LLVMBuildPtrToInt(c->builder, arg, param, ""); break; case LLVMStructTypeKind: pony_assert(LLVMGetTypeKind(arg_type) == LLVMStructTypeKind); return arg; default: {} } pony_assert(false); return NULL; }
LLVMValueRef gen_goto(struct node *ast) { LLVMValueRef branch, func; LLVMBasicBlockRef next_block; int i; branch = LLVMBuildIndirectBr(builder, LLVMBuildIntToPtr(builder, codegen(ast->one), TYPE_LABEL, ""), label_count); for (i = 0; i < label_count; i++) LLVMAddDestination(branch, label_blocks[i]); func = LLVMGetBasicBlockParent(LLVMGetInsertBlock(builder)); next_block = LLVMAppendBasicBlock(func, ""); LLVMPositionBuilderAtEnd(builder, next_block); return NULL; }
static LLVMValueRef cast_ffi_arg(compile_t* c, LLVMValueRef arg, LLVMTypeRef param) { if(arg == NULL) return NULL; LLVMTypeRef arg_type = LLVMTypeOf(arg); if(param == c->i1) { // If the parameter is an i1, it must be from an LLVM intrinsic. In that // case, the argument must be a Bool encoded as an ibool. if(arg_type != c->ibool) return NULL; // Truncate the Bool's i8 representation to i1. return LLVMBuildTrunc(c->builder, arg, c->i1, ""); } switch(LLVMGetTypeKind(param)) { case LLVMPointerTypeKind: { if(LLVMGetTypeKind(arg_type) == LLVMIntegerTypeKind) arg = LLVMBuildIntToPtr(c->builder, arg, param, ""); else arg = LLVMBuildBitCast(c->builder, arg, param, ""); break; } default: {} } return arg; }
static LLVMValueRef rvalue_to_lvalue(LLVMValueRef rvalue) { rvalue = LLVMBuildShl(builder, rvalue, CONST(WORDPOW), ""); return LLVMBuildIntToPtr(builder, rvalue, TYPE_PTR, ""); }
/* * Do a cached lookup. * * Returns (vectors of) 4x8 rgba aos value */ LLVMValueRef lp_build_fetch_cached_texels(struct gallivm_state *gallivm, const struct util_format_description *format_desc, unsigned n, LLVMValueRef base_ptr, LLVMValueRef offset, LLVMValueRef i, LLVMValueRef j, LLVMValueRef cache) { LLVMBuilderRef builder = gallivm->builder; unsigned count, low_bit, log2size; LLVMValueRef color, offset_stored, addr, ptr_addrtrunc, tmp; LLVMValueRef ij_index, hash_index, hash_mask, block_index; LLVMTypeRef i8t = LLVMInt8TypeInContext(gallivm->context); LLVMTypeRef i32t = LLVMInt32TypeInContext(gallivm->context); LLVMTypeRef i64t = LLVMInt64TypeInContext(gallivm->context); struct lp_type type; struct lp_build_context bld32; memset(&type, 0, sizeof type); type.width = 32; type.length = n; assert(format_desc->block.width == 4); assert(format_desc->block.height == 4); lp_build_context_init(&bld32, gallivm, type); /* * compute hash - we use direct mapped cache, the hash function could * be better but it needs to be simple * per-element: * compare offset with offset stored at tag (hash) * if not equal decode/store block, update tag * extract color from cache * assemble result vector */ /* TODO: not ideal with 32bit pointers... */ low_bit = util_logbase2(format_desc->block.bits / 8); log2size = util_logbase2(LP_BUILD_FORMAT_CACHE_SIZE); addr = LLVMBuildPtrToInt(builder, base_ptr, i64t, ""); ptr_addrtrunc = LLVMBuildPtrToInt(builder, base_ptr, i32t, ""); ptr_addrtrunc = lp_build_broadcast_scalar(&bld32, ptr_addrtrunc); /* For the hash function, first mask off the unused lowest bits. Then just do some xor with address bits - only use lower 32bits */ ptr_addrtrunc = LLVMBuildAdd(builder, offset, ptr_addrtrunc, ""); ptr_addrtrunc = LLVMBuildLShr(builder, ptr_addrtrunc, lp_build_const_int_vec(gallivm, type, low_bit), ""); /* This only really makes sense for size 64,128,256 */ hash_index = ptr_addrtrunc; ptr_addrtrunc = LLVMBuildLShr(builder, ptr_addrtrunc, lp_build_const_int_vec(gallivm, type, 2*log2size), ""); hash_index = LLVMBuildXor(builder, ptr_addrtrunc, hash_index, ""); tmp = LLVMBuildLShr(builder, hash_index, lp_build_const_int_vec(gallivm, type, log2size), ""); hash_index = LLVMBuildXor(builder, hash_index, tmp, ""); hash_mask = lp_build_const_int_vec(gallivm, type, LP_BUILD_FORMAT_CACHE_SIZE - 1); hash_index = LLVMBuildAnd(builder, hash_index, hash_mask, ""); ij_index = LLVMBuildShl(builder, i, lp_build_const_int_vec(gallivm, type, 2), ""); ij_index = LLVMBuildAdd(builder, ij_index, j, ""); block_index = LLVMBuildShl(builder, hash_index, lp_build_const_int_vec(gallivm, type, 4), ""); block_index = LLVMBuildAdd(builder, ij_index, block_index, ""); if (n > 1) { color = LLVMGetUndef(LLVMVectorType(i32t, n)); for (count = 0; count < n; count++) { LLVMValueRef index, cond, colorx; LLVMValueRef block_indexx, hash_indexx, addrx, offsetx, ptr_addrx; struct lp_build_if_state if_ctx; index = lp_build_const_int32(gallivm, count); offsetx = LLVMBuildExtractElement(builder, offset, index, ""); addrx = LLVMBuildZExt(builder, offsetx, i64t, ""); addrx = LLVMBuildAdd(builder, addrx, addr, ""); block_indexx = LLVMBuildExtractElement(builder, block_index, index, ""); hash_indexx = LLVMBuildLShr(builder, block_indexx, lp_build_const_int32(gallivm, 4), ""); offset_stored = lookup_tag_data(gallivm, cache, hash_indexx); cond = LLVMBuildICmp(builder, LLVMIntNE, offset_stored, addrx, ""); lp_build_if(&if_ctx, gallivm, cond); { ptr_addrx = LLVMBuildIntToPtr(builder, addrx, LLVMPointerType(i8t, 0), ""); update_cached_block(gallivm, format_desc, ptr_addrx, hash_indexx, cache); #if LP_BUILD_FORMAT_CACHE_DEBUG update_cache_access(gallivm, cache, 1, LP_BUILD_FORMAT_CACHE_MEMBER_ACCESS_MISS); #endif } lp_build_endif(&if_ctx); colorx = lookup_cached_pixel(gallivm, cache, block_indexx); color = LLVMBuildInsertElement(builder, color, colorx, lp_build_const_int32(gallivm, count), ""); } } else { LLVMValueRef cond; struct lp_build_if_state if_ctx; tmp = LLVMBuildZExt(builder, offset, i64t, ""); addr = LLVMBuildAdd(builder, tmp, addr, ""); offset_stored = lookup_tag_data(gallivm, cache, hash_index); cond = LLVMBuildICmp(builder, LLVMIntNE, offset_stored, addr, ""); lp_build_if(&if_ctx, gallivm, cond); { tmp = LLVMBuildIntToPtr(builder, addr, LLVMPointerType(i8t, 0), ""); update_cached_block(gallivm, format_desc, tmp, hash_index, cache); #if LP_BUILD_FORMAT_CACHE_DEBUG update_cache_access(gallivm, cache, 1, LP_BUILD_FORMAT_CACHE_MEMBER_ACCESS_MISS); #endif } lp_build_endif(&if_ctx); color = lookup_cached_pixel(gallivm, cache, block_index); } #if LP_BUILD_FORMAT_CACHE_DEBUG update_cache_access(gallivm, cache, n, LP_BUILD_FORMAT_CACHE_MEMBER_ACCESS_TOTAL); #endif return LLVMBuildBitCast(builder, color, LLVMVectorType(i8t, n * 4), ""); }
void genprim_string_serialise(compile_t* c, reach_type_t* t) { // Generate the serialise function. t->serialise_fn = codegen_addfun(c, genname_serialise(t->name), c->serialise_type); codegen_startfun(c, t->serialise_fn, NULL, NULL); LLVMSetFunctionCallConv(t->serialise_fn, LLVMCCallConv); LLVMValueRef ctx = LLVMGetParam(t->serialise_fn, 0); LLVMValueRef arg = LLVMGetParam(t->serialise_fn, 1); LLVMValueRef addr = LLVMGetParam(t->serialise_fn, 2); LLVMValueRef offset = LLVMGetParam(t->serialise_fn, 3); LLVMValueRef mut = LLVMGetParam(t->serialise_fn, 4); LLVMValueRef object = LLVMBuildBitCast(c->builder, arg, t->structure_ptr, ""); LLVMValueRef offset_addr = LLVMBuildAdd(c->builder, LLVMBuildPtrToInt(c->builder, addr, c->intptr, ""), offset, ""); genserialise_typeid(c, t, offset_addr); // Don't serialise our contents if we are opaque. LLVMBasicBlockRef body_block = codegen_block(c, "body"); LLVMBasicBlockRef post_block = codegen_block(c, "post"); LLVMValueRef test = LLVMBuildICmp(c->builder, LLVMIntNE, mut, LLVMConstInt(c->i32, PONY_TRACE_OPAQUE, false), ""); LLVMBuildCondBr(c->builder, test, body_block, post_block); LLVMPositionBuilderAtEnd(c->builder, body_block); // Write the size, and rewrite alloc to be size + 1. LLVMValueRef size = field_value(c, object, 1); LLVMValueRef size_loc = field_loc(c, offset_addr, t->structure, c->intptr, 1); LLVMBuildStore(c->builder, size, size_loc); LLVMValueRef alloc = LLVMBuildAdd(c->builder, size, LLVMConstInt(c->intptr, 1, false), ""); LLVMValueRef alloc_loc = field_loc(c, offset_addr, t->structure, c->intptr, 2); LLVMBuildStore(c->builder, alloc, alloc_loc); // Write the pointer. LLVMValueRef ptr = field_value(c, object, 3); LLVMValueRef args[5]; args[0] = ctx; args[1] = ptr; LLVMValueRef ptr_offset = gencall_runtime(c, "pony_serialise_offset", args, 2, ""); LLVMValueRef ptr_loc = field_loc(c, offset_addr, t->structure, c->intptr, 3); LLVMBuildStore(c->builder, ptr_offset, ptr_loc); // Serialise the string contents. LLVMValueRef ptr_offset_addr = LLVMBuildAdd(c->builder, LLVMBuildPtrToInt(c->builder, addr, c->intptr, ""), ptr_offset, ""); args[0] = LLVMBuildIntToPtr(c->builder, ptr_offset_addr, c->void_ptr, ""); args[1] = LLVMBuildBitCast(c->builder, field_value(c, object, 3), c->void_ptr, ""); args[2] = alloc; args[3] = LLVMConstInt(c->i32, 1, false); args[4] = LLVMConstInt(c->i1, 0, false); if(target_is_ilp32(c->opt->triple)) { gencall_runtime(c, "llvm.memcpy.p0i8.p0i8.i32", args, 5, ""); } else { gencall_runtime(c, "llvm.memcpy.p0i8.p0i8.i64", args, 5, ""); } LLVMBuildBr(c->builder, post_block); LLVMPositionBuilderAtEnd(c->builder, post_block); LLVMBuildRetVoid(c->builder); codegen_finishfun(c); }
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; // 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. 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, &g, decl_params, err); } else if(!strncmp(f_name, "llvm.", 5)) { // Intrinsic, so use the exact types we supply. func = declare_ffi(c, f_name, &g, args, err); } else { // Make it varargs. func = declare_ffi_vararg(c, f_name, &g, 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 && (LLVMGetTypeKind(f_params[i]) == LLVMPointerTypeKind)) { if(LLVMGetTypeKind(LLVMTypeOf(f_args[i])) == LLVMIntegerTypeKind) f_args[i] = LLVMBuildIntToPtr(c->builder, f_args[i], f_params[i], ""); else f_args[i] = LLVMBuildBitCast(c->builder, 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; 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, ""); 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 g.instance; return result; }
void genprim_array_serialise(compile_t* c, reach_type_t* t) { // Generate the serialise function. t->serialise_fn = codegen_addfun(c, genname_serialise(t->name), c->serialise_type); codegen_startfun(c, t->serialise_fn, NULL, NULL); LLVMSetFunctionCallConv(t->serialise_fn, LLVMCCallConv); LLVMValueRef ctx = LLVMGetParam(t->serialise_fn, 0); LLVMValueRef arg = LLVMGetParam(t->serialise_fn, 1); LLVMValueRef addr = LLVMGetParam(t->serialise_fn, 2); LLVMValueRef offset = LLVMGetParam(t->serialise_fn, 3); LLVMValueRef mut = LLVMGetParam(t->serialise_fn, 4); LLVMValueRef object = LLVMBuildBitCast(c->builder, arg, t->structure_ptr, ""); LLVMValueRef offset_addr = LLVMBuildAdd(c->builder, LLVMBuildPtrToInt(c->builder, addr, c->intptr, ""), offset, ""); genserialise_typeid(c, t, offset_addr); // Don't serialise our contents if we are opaque. LLVMBasicBlockRef body_block = codegen_block(c, "body"); LLVMBasicBlockRef post_block = codegen_block(c, "post"); LLVMValueRef test = LLVMBuildICmp(c->builder, LLVMIntNE, mut, LLVMConstInt(c->i32, PONY_TRACE_OPAQUE, false), ""); LLVMBuildCondBr(c->builder, test, body_block, post_block); LLVMPositionBuilderAtEnd(c->builder, body_block); // Write the size twice, effectively rewriting alloc to be the same as size. LLVMValueRef size = field_value(c, object, 1); LLVMValueRef size_loc = field_loc(c, offset_addr, t->structure, c->intptr, 1); LLVMBuildStore(c->builder, size, size_loc); LLVMValueRef alloc_loc = field_loc(c, offset_addr, t->structure, c->intptr, 2); LLVMBuildStore(c->builder, size, alloc_loc); // Write the pointer. LLVMValueRef ptr = field_value(c, object, 3); // The resulting offset will only be invalid (i.e. have the high bit set) if // the size is zero. For an opaque array, we don't serialise the contents, // so we don't get here, so we don't end up with an invalid offset. LLVMValueRef args[5]; args[0] = ctx; args[1] = ptr; LLVMValueRef ptr_offset = gencall_runtime(c, "pony_serialise_offset", args, 2, ""); LLVMValueRef ptr_loc = field_loc(c, offset_addr, t->structure, c->intptr, 3); LLVMBuildStore(c->builder, ptr_offset, ptr_loc); LLVMValueRef ptr_offset_addr = LLVMBuildAdd(c->builder, ptr_offset, LLVMBuildPtrToInt(c->builder, addr, c->intptr, ""), ""); // Serialise elements. ast_t* typeargs = ast_childidx(t->ast, 2); ast_t* typearg = ast_child(typeargs); reach_type_t* t_elem = reach_type(c->reach, typearg); size_t abisize = (size_t)LLVMABISizeOfType(c->target_data, t_elem->use_type); LLVMValueRef l_size = LLVMConstInt(c->intptr, abisize, false); if((t_elem->underlying == TK_PRIMITIVE) && (t_elem->primitive != NULL)) { // memcpy machine words args[0] = LLVMBuildIntToPtr(c->builder, ptr_offset_addr, c->void_ptr, ""); args[1] = LLVMBuildBitCast(c->builder, ptr, c->void_ptr, ""); args[2] = LLVMBuildMul(c->builder, size, l_size, ""); args[3] = LLVMConstInt(c->i32, 1, false); args[4] = LLVMConstInt(c->i1, 0, false); if(target_is_ilp32(c->opt->triple)) { gencall_runtime(c, "llvm.memcpy.p0i8.p0i8.i32", args, 5, ""); } else { gencall_runtime(c, "llvm.memcpy.p0i8.p0i8.i64", args, 5, ""); } } else { ptr = LLVMBuildBitCast(c->builder, ptr, LLVMPointerType(t_elem->use_type, 0), ""); LLVMBasicBlockRef entry_block = LLVMGetInsertBlock(c->builder); LLVMBasicBlockRef cond_block = codegen_block(c, "cond"); LLVMBasicBlockRef body_block = codegen_block(c, "body"); LLVMBasicBlockRef post_block = codegen_block(c, "post"); LLVMValueRef offset_var = LLVMBuildAlloca(c->builder, c->intptr, ""); LLVMBuildStore(c->builder, ptr_offset_addr, offset_var); LLVMBuildBr(c->builder, cond_block); // While the index is less than the size, serialise an element. The // initial index when coming from the entry block is zero. LLVMPositionBuilderAtEnd(c->builder, cond_block); LLVMValueRef phi = LLVMBuildPhi(c->builder, c->intptr, ""); LLVMValueRef zero = LLVMConstInt(c->intptr, 0, false); LLVMAddIncoming(phi, &zero, &entry_block, 1); LLVMValueRef test = LLVMBuildICmp(c->builder, LLVMIntULT, phi, size, ""); LLVMBuildCondBr(c->builder, test, body_block, post_block); // The phi node is the index. Get the element and serialise it. LLVMPositionBuilderAtEnd(c->builder, body_block); LLVMValueRef elem_ptr = LLVMBuildGEP(c->builder, ptr, &phi, 1, ""); ptr_offset_addr = LLVMBuildLoad(c->builder, offset_var, ""); genserialise_element(c, t_elem, false, ctx, elem_ptr, ptr_offset_addr); ptr_offset_addr = LLVMBuildAdd(c->builder, ptr_offset_addr, l_size, ""); LLVMBuildStore(c->builder, ptr_offset_addr, offset_var); // Add one to the phi node and branch back to the cond block. LLVMValueRef one = LLVMConstInt(c->intptr, 1, false); LLVMValueRef inc = LLVMBuildAdd(c->builder, phi, one, ""); body_block = LLVMGetInsertBlock(c->builder); LLVMAddIncoming(phi, &inc, &body_block, 1); LLVMBuildBr(c->builder, cond_block); LLVMPositionBuilderAtEnd(c->builder, post_block); } LLVMBuildBr(c->builder, post_block); LLVMPositionBuilderAtEnd(c->builder, post_block); LLVMBuildRetVoid(c->builder); codegen_finishfun(c); }