// Copyright (c) 2022 Christoffer Lerno. All rights reserved. // Use of this source code is governed by a LGPLv3.0 // a copy of which can be found in the LICENSE file. #include "tilde_internal.h" TB_Reg tilde_get_const_int(TildeContext *c, Type *type, uint64_t i) { return type_is_signed(type) ? tb_inst_sint(c->f, tildetype(type), (int64_t)i) : tb_inst_uint(c->f, tildetype(type), i); } TB_Reg tilde_get_const_float(TildeContext *c, Type *type, double d) { return type_size(type) == 4 ? tb_inst_float32(c->f, (float)d) : tb_inst_float64(c->f, d); } TB_Register tilde_get_zero(TildeContext *c, Type *type) { type = type_lowering(type); TB_DataType data_type = tildetype(type); switch (data_type.type) { case TB_INT: return type_is_signed(type) ? tb_inst_sint(c->f, data_type, 0) : tb_inst_uint(c->f, data_type, 0); case TB_FLOAT: return type->type_kind == TYPE_F32 ? tb_inst_float32(c->f, 0.0f) : tb_inst_float64(c->f, 0.0); case TB_PTR: return tb_inst_ptr(c->f, 0); default: UNREACHABLE; } } static void tilde_emit_const_expr(TildeContext *c, TBEValue *value, Expr *expr) { Type *type = type_reduced_from_expr(expr)->canonical; switch (expr->const_expr.const_kind) { case CONST_BYTES: TODO case CONST_INTEGER: { Int128 i = expr->const_expr.ixx.i; switch (expr->const_expr.ixx.type) { case TYPE_I128: case TYPE_U128: { uint64_t words[2] = { i.low, i.high }; TODO } default: value_set(value, tilde_get_const_int(c, type, i.low), type); return; } } case CONST_FLOAT: value_set(value, tilde_get_const_float(c, type, expr->const_expr.fxx.f), type); return; case CONST_POINTER: if (!expr->const_expr.ptr) { value_set(value, tb_inst_ptr(c->f, 0), type); } else { value_set(value, tb_inst_ptr(c->f, expr->const_expr.ptr), type); } return; case CONST_BOOL: value_set(value, tb_inst_bool(c->f, expr->const_expr.b), type_bool); return; case CONST_STRING: { TODO /* Type *str_type = type_lowering(expr->type); bool is_array = type_flat_is_char_array(str_type); if (llvm_is_local_eval(c) || !is_array) { ArraySize strlen = expr->const_expr.bytes.len; ArraySize size = expr->const_expr.bytes.len + 1; if (type_flat_is_char_array(expr->type) && type->array.len > size) size = type->array.len; LLVMValueRef global_name = llvm_add_global_raw(c, ".str", LLVMArrayType(llvm_get_type(c, type_char), size), 1); llvm_set_private_linkage(global_name); LLVMSetUnnamedAddress(global_name, LLVMGlobalUnnamedAddr); LLVMSetGlobalConstant(global_name, 1); LLVMValueRef string = llvm_get_zstring(c, expr->const_expr.bytes.ptr, expr->const_expr.bytes.len); if (size > strlen + 1) { LLVMValueRef trailing_zeros = llvm_get_zero_raw(LLVMArrayType(c->byte_type, size - strlen - 1)); LLVMValueRef values[2] = { string, trailing_zeros }; string = llvm_get_packed_struct(c, values, 2); } LLVMSetInitializer(global_name, string); if (is_array) { global_name = llvm_emit_bitcast_ptr(c, global_name, type); llvm_value_set_address(be_value, global_name, type, 1); } else { global_name = llvm_emit_bitcast(c, global_name, type); llvm_value_set(be_value, global_name, type); } return; } ArraySize array_len = type->array.len; ArraySize size = expr->const_expr.bytes.len + 1; bool zero_terminate = array_len == size; LLVMValueRef string; if (array_len <= size) { if (zero_terminate) { string = llvm_get_zstring(c, expr->const_expr.bytes.ptr, expr->const_expr.bytes.len); } else { string = llvm_get_bytes(c, expr->const_expr.bytes.ptr, array_len); } } else { char *buffer = ccalloc(1, array_len); memcpy(buffer, expr->const_expr.bytes.ptr, expr->const_expr.bytes.len); string = llvm_get_bytes(c, buffer, array_len); } llvm_value_set(be_value, string, type);*/ return; } case CONST_TYPEID: TODO //llvm_emit_typeid(c, be_value, expr->const_expr.typeid); return; case CONST_ERR: { TODO /* Decl *decl = expr->const_expr.enum_err_val; LLVMValueRef value; if (decl) { value = LLVMBuildPtrToInt(c->builder, llvm_get_ref(c, decl), llvm_get_type(c, type_anyfault), ""); } else { value = llvm_get_zero(c, type_anyfault); } llvm_value_set(be_value, value, type_anyfault);*/ return; } case CONST_ENUM: value_set(value, tilde_get_const_int(c, type, expr->const_expr.enum_err_val->enum_constant.ordinal), type); return; case CONST_INITIALIZER: TODO //llvm_emit_const_initializer_list_expr(c, be_value, expr); return; case CONST_MEMBER: case CONST_UNTYPED_LIST: UNREACHABLE } UNREACHABLE } void tilde_emit_parameter(TildeContext *c, TB_Reg *args, unsigned *arg_count_ref, ABIArgInfo *info, TBEValue *be_value, Type *type) { type = type_lowering(type); assert(be_value->type->canonical == type); switch (info->kind) { case ABI_ARG_IGNORE: // Skip. return; case ABI_ARG_INDIRECT: { // If we want we could optimize for structs by doing it by reference here. assert(info->indirect.alignment == type_abi_alignment(type) || info->attributes.realign); if (info->attributes.by_val && value_is_addr(be_value) && info->indirect.alignment <= be_value->alignment) { value_fold_optional(c, be_value); args[(*arg_count_ref)++] = be_value->reg; return; } TB_Reg indirect = tilde_emit_alloca(c, type, info->indirect.alignment); tilde_store_to_ptr_aligned(c, indirect, be_value, info->indirect.alignment); args[(*arg_count_ref)++] = indirect; return; } case ABI_ARG_DIRECT: args[(*arg_count_ref)++] = tilde_load_value_store(c, be_value); return; case ABI_ARG_DIRECT_SPLIT_STRUCT: { TODO; /*-- LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info); assert(coerce_type && coerce_type != llvm_get_type(c, type)); AlignSize target_alignment = llvm_abi_alignment(c, coerce_type); AlignSize alignment; LLVMValueRef cast = llvm_emit_coerce_alignment(c, be_value, coerce_type, target_alignment, &alignment); LLVMTypeRef element = llvm_get_type(c, info->direct_struct_expand.type); for (unsigned idx = 0; idx < info->direct_struct_expand.elements; idx++) { AlignSize load_align; LLVMValueRef element_ptr = llvm_emit_struct_gep_raw(c, cast, coerce_type, idx, alignment, &load_align); args[(*arg_count_ref)++] = llvm_load(c, element, element_ptr, load_align, ""); } return;*/ } case ABI_ARG_DIRECT_COERCE: { TODO; /* LLVMTypeRef coerce_type = llvm_get_type(c, info->direct_coerce_type); if (coerce_type == llvm_get_type(c, type)) { args[(*arg_count_ref)++] = llvm_load_value_store(c, be_value); return; } args[(*arg_count_ref)++] = llvm_emit_coerce(c, coerce_type, be_value, type); return;--*/ } case ABI_ARG_DIRECT_COERCE_INT: { TODO; /*-- LLVMTypeRef coerce_type = LLVMIntTypeInContext(c->context, type_size(type) * 8); if (coerce_type == llvm_get_type(c, type)) { args[(*arg_count_ref)++] = llvm_load_value_store(c, be_value); return; } args[(*arg_count_ref)++] = llvm_emit_coerce(c, coerce_type, be_value, type); return; --*/ } case ABI_ARG_DIRECT_PAIR: { TODO; /*- assert(type_flatten(be_value->type) == be_value->type); LLVMTypeRef original_type = llvm_get_type(c, be_value->type); LLVMTypeRef struct_type = llvm_get_coerce_type(c, info); AlignSize alignment; if (llvm_types_are_similar(original_type, struct_type)) { // Optimization assert(LLVMGetTypeKind(original_type) == LLVMStructTypeKind && LLVMCountStructElementTypes(original_type) == 2); if (llvm_value_is_addr(be_value)) { LLVMValueRef ptr = llvm_emit_struct_gep_raw(c, be_value->value, original_type, 0, be_value->alignment, &alignment); args[(*arg_count_ref)++] = llvm_load(c, LLVMStructGetTypeAtIndex(original_type, 0), ptr, alignment, "lo"); ptr = llvm_emit_struct_gep_raw(c, be_value->value, original_type, 1, be_value->alignment, &alignment); args[(*arg_count_ref)++] = llvm_load(c, LLVMStructGetTypeAtIndex(original_type, 1), ptr, alignment, "hi"); return; } LLVMValueRef val = be_value->value; // Maybe it's just created? Let's optimize codegen. if (!LLVMGetFirstUse(val) && LLVMIsAInsertValueInst(val) && LLVMIsAInsertValueInst( LLVMGetPreviousInstruction(val))) { LLVMValueRef prev = LLVMGetPreviousInstruction(val); // Isn't this a second insert? if (LLVMGetOperand(val, 0) != prev) goto NO_OPT; // Is it used in between? if (LLVMGetNextUse(LLVMGetFirstUse(prev))) goto NO_OPT; // No, then we can replace the instructions with the values. LLVMValueRef first_val = LLVMGetOperand(prev, 1); LLVMValueRef second_val = LLVMGetOperand(val, 1); LLVMInstructionEraseFromParent(val); LLVMInstructionEraseFromParent(prev); args[(*arg_count_ref)++] = first_val; args[(*arg_count_ref)++] = second_val; return; } NO_OPT: args[(*arg_count_ref)++] = llvm_emit_extract_value(c, be_value->value, 0); args[(*arg_count_ref)++] = llvm_emit_extract_value(c, be_value->value, 1); return; } llvm_value_addr(c, be_value); REMINDER("Handle invalid alignment"); // Here we do the following transform: // struct -> { lo, hi } -> lo, hi LLVMTypeRef lo = llvm_abi_type(c, info->direct_pair.lo); LLVMTypeRef hi = llvm_abi_type(c, info->direct_pair.hi); AlignSize struct_align; LLVMValueRef cast = llvm_emit_coerce_alignment(c, be_value, struct_type, llvm_abi_alignment(c, struct_type), &struct_align); // Get the lo value. LLVMValueRef lo_ptr = llvm_emit_struct_gep_raw(c, cast, struct_type, 0, struct_align, &alignment); args[(*arg_count_ref)++] = llvm_load(c, lo, lo_ptr, alignment, "lo"); // Get the hi value. LLVMValueRef hi_ptr = llvm_emit_struct_gep_raw(c, cast, struct_type, 1, struct_align, &alignment); args[(*arg_count_ref)++] = llvm_load(c, hi, hi_ptr, alignment, "hi"); return;--*/ } case ABI_ARG_EXPAND_COERCE: { TODO /*-- // Move this to an address (if needed) llvm_value_addr(c, be_value); LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info); AlignSize alignment; LLVMValueRef temp = llvm_emit_coerce_alignment(c, be_value, coerce_type, llvm_abi_alignment(c, coerce_type), &alignment); AlignSize align; LLVMValueRef gep_first = llvm_emit_struct_gep_raw(c, temp, coerce_type, info->coerce_expand.lo_index, alignment, &align); args[(*arg_count_ref)++] = llvm_load(c, llvm_abi_type(c, info->coerce_expand.lo), gep_first, align, ""); if (abi_type_is_valid(info->coerce_expand.hi)) { LLVMValueRef gep_second = llvm_emit_struct_gep_raw(c, temp, coerce_type, info->coerce_expand.hi_index, alignment, &align); args[(*arg_count_ref)++] = llvm_load(c, llvm_abi_type(c, info->coerce_expand.hi), gep_second, align, ""); } return;--*/ } case ABI_ARG_EXPAND: { // Move this to an address (if needed) value_addr(c, be_value); TODO /*--tilde_expand_type_to_args(c, type, be_value.reg, args, arg_count_ref, be_value->alignment); // Expand the padding here. if (info->expand.padding_type) { args[(*arg_count_ref)++] = TB_NULL_REG; } return;--*/ } } } void tilde_emit_raw_call(TildeContext *c, TBEValue *result_value, FunctionPrototype *prototype, TB_FunctionPrototype *func_type, TB_Function *func, TB_Reg func_ptr, TB_Reg *args, unsigned arg_count, int inline_flag, TB_Reg error_var, bool sret_return, TBEValue *synthetic_return_param) { ABIArgInfo *ret_info = prototype->ret_abi_info; Type *call_return_type = prototype->abi_ret_type; TB_Reg call_value; if (func_ptr) { TODO } else { call_value = tb_inst_call(c->f, tildetype(call_return_type), (TB_Symbol *)func, arg_count, args); } switch (inline_flag) { case -1: TODO // llvm_attribute_add_call(c, call_value, attribute_id.noinline, -1, 0); break; case 1: TODO // llvm_attribute_add_call(c, call_value, attribute_id.alwaysinline, -1, 0); break; default: break; } assert(!prototype->ret_by_ref || prototype->ret_by_ref_abi_info->kind != ABI_ARG_INDIRECT); /* llvm_add_abi_call_attributes(c, call_value, vec_size(prototype->param_types), prototype->abi_args); if (prototype->abi_varargs) { llvm_add_abi_call_attributes(c, call_value, vec_size(prototype->varargs), prototype->abi_varargs); }*/ // 11. Process the return value. switch (ret_info->kind) { case ABI_ARG_EXPAND: case ABI_ARG_DIRECT_SPLIT_STRUCT: UNREACHABLE case ABI_ARG_IGNORE: // 12. Basically void returns or empty structs. // Here we know we don't have an optional or any return value that can be used. assert(!prototype->is_optional && "Optional should have produced a return value."); *result_value = (TBEValue) { .type = type_void, .kind = TBE_VALUE }; return; case ABI_ARG_INDIRECT: TODO /* llvm_attribute_add_call_type(c, call_value, attribute_id.sret, 1, llvm_get_type(c, ret_info->indirect.type)); llvm_attribute_add_call(c, call_value, attribute_id.align, 1, ret_info->indirect.alignment); // 13. Indirect, that is passing the result through an out parameter. // 13a. In the case of an already present error_var, we don't need to do a load here. if (error_var || sret_return) break; // 13b. If not it will be contained in a be_value that is an address // so we don't need to do anything more. assert(result_value->kind == BE_ADDRESS); break; --*/ case ABI_ARG_DIRECT_PAIR: { TODO /* // 14. A direct pair, in this case the data is stored like { lo, hi } // For example we might have { int, int, short, short, int }, // this then gets bitcast to { long, long }, so we recover it by loading // { long, long } into memory, then performing a bitcast to { int, int, short, short, int } // 14a. Generate the type. LLVMTypeRef lo = llvm_abi_type(c, ret_info->direct_pair.lo); LLVMTypeRef hi = llvm_abi_type(c, ret_info->direct_pair.hi); LLVMTypeRef struct_type = llvm_get_twostruct(c, lo, hi); // 14b. Use the coerce method to go from the struct to the actual type // by storing the { lo, hi } struct to memory, then loading it // again using a bitcast. llvm_emit_convert_value_from_coerced(c, result_value, struct_type, call_value, call_return_type); break; --*/ } case ABI_ARG_EXPAND_COERCE: { TODO /* // 15. Expand-coerce, this is similar to "direct pair", but looks like this: // { lo, hi } set into { pad, lo, pad, hi } -> original type. // 15a. Create memory to hold the return type. LLVMValueRef ret = llvm_emit_alloca_aligned(c, call_return_type, ""); llvm_value_set_address_abi_aligned(result_value, ret, call_return_type); // 15b. "Convert" this return type pointer in memory to our coerce type which is { pad, lo, pad, hi } LLVMTypeRef coerce_type = llvm_get_coerce_type(c, ret_info); LLVMValueRef coerce = LLVMBuildBitCast(c->builder, ret, coerce_type, ""); // 15d. Find the address to the low value AlignSize alignment; LLVMValueRef lo = llvm_emit_struct_gep_raw(c, coerce, coerce_type, ret_info->coerce_expand.lo_index, type_abi_alignment(call_return_type), &alignment); // 15e. If there is only a single field, we simply store the value, // so { lo } set into { pad, lo, pad } -> original type. if (!abi_type_is_valid(ret_info->coerce_expand.hi)) { // Here we do a store to call -> lo (leaving the rest undefined) llvm_store_to_ptr_raw_aligned(c, lo, call_value, alignment); break; } // 15g. We can now extract { lo, hi } to lo_value and hi_value. LLVMValueRef lo_value = llvm_emit_extract_value(c, call_value, 0); LLVMValueRef hi_value = llvm_emit_extract_value(c, call_value, 1); // 15h. Store lo_value into the { pad, lo, pad, hi } struct. llvm_store_to_ptr_raw_aligned(c, lo, lo_value, alignment); // 15i. Calculate the address to the high value (like for the low in 15d. LLVMValueRef hi = llvm_emit_struct_gep_raw(c, coerce, coerce_type, ret_info->coerce_expand.hi_index, type_abi_alignment(call_return_type), &alignment); // 15h. Store the high value. llvm_store_to_ptr_raw_aligned(c, hi, hi_value, alignment); */ break; } case ABI_ARG_DIRECT: value_set(result_value, call_value, call_return_type); break; case ABI_ARG_DIRECT_COERCE_INT: { // 16. A direct coerce, this is basically "call result" bitcast return type. // 16a. Get the type of the return. TB_DataType coerce = tilde_get_int_type_of_bytesize(type_size(call_return_type)); // 16b. If we don't have any coerce type, or the actual LLVM types are the same, we're done. TB_DataType ret_type = tildetype(call_return_type); if (coerce.raw == ret_type.raw) { // 16c. We just set as a value in be_value. value_set(result_value, call_value, call_return_type); break; } // 16c. We use a normal bitcast coerce. TODO // tilde_emit_convert_value_from_coerced(c, result_value, coerce, call_value, call_return_type); break; } case ABI_ARG_DIRECT_COERCE: { TODO /*--- // 16. A direct coerce, this is basically "call result" bitcast return type. // 16a. Get the type of the return. LLVMTypeRef coerce = llvm_get_type(c, ret_info->direct_coerce_type); // 16b. If we don't have any coerce type, or the actual LLVM types are the same, we're done. if (coerce == llvm_get_type(c, call_return_type)) { // 16c. We just set as a value in be_value. llvm_value_set(result_value, call_value, call_return_type); break; } // 16c. We use a normal bitcast coerce. llvm_emit_convert_value_from_coerced(c, result_value, coerce, call_value, call_return_type); break; */ } } // 17. Handle optionals. if (sret_return) { *result_value = (TBEValue) { .type = type_void, .kind = TBE_VALUE }; return; } if (prototype->is_optional) { TBEValue no_err; // Emit the current stack into the thread local or things will get messed up. if (c->debug.last_ptr) tilde_store_to_ptr_raw_aligned(c, type_voidptr, c->debug.last_ptr, c->debug.stack_slot, type_alloca_alignment(type_voidptr)); // 17a. If we used the error var as the indirect recipient, then that will hold the error. // otherwise it's whatever value in be_value. TBEValue error_holder = *result_value; if (error_var) { value_set_address_abi_aligned(&error_holder, c->opt_var, type_anyfault); } TB_Reg stored_error; if (error_var) { stored_error = c->opt_var; c->opt_var = TB_NULL_REG; } tilde_emit_jump_to_optional_exit(c, tilde_load_value(c, &error_holder)); if (error_var) { c->opt_var = stored_error; } // 17g. If void, be_value contents should be skipped. if (!prototype->ret_by_ref) { *result_value = (TBEValue) { .type = type_void, .kind = TBE_VALUE }; return; } // 17h. Assign the return param to be_value. *result_value = *synthetic_return_param; return; } // Emit the current stack into the thread local or things will get messed up. if (c->debug.last_ptr) tilde_store_to_ptr_raw_aligned(c, type_voidptr, c->debug.last_ptr, c->debug.stack_slot, type_alloca_alignment(type_voidptr)); // 17i. The simple case here is where there is a normal return. // In this case be_value already holds the result } static void tilde_emit_call_expr(TildeContext *c, TBEValue *result_value, Expr *expr, TBEValue *target) { if (expr->call_expr.is_builtin) { TODO // llvm_emit_builtin_call(c, result_value, expr); return; } REMINDER("Debug stack"); /* if (c->debug.stack_slot_row) { llvm_store_to_ptr_raw_aligned(c, c->debug.stack_slot_row, llvm_const_int(c, type_uint, expr->span.row), type_abi_alignment(type_uint)); }*/ TB_FunctionPrototype *func_type; TB_Function *func = NULL; TB_Reg func_ptr = TB_NULL_REG; TBEValue temp_value; bool always_inline = false; FunctionPrototype *prototype; // 1. Call through a pointer. if (!expr->call_expr.is_func_ref) { Expr *function = exprptr(expr->call_expr.function); // 1a. Find the pointee type for the function pointer: Type *type = function->type->canonical->pointer; // 1b. Find the type signature using the underlying pointer. prototype = type->function.prototype; // 1c. Evaluate the pointer expression. TBEValue func_value; tilde_emit_expr(c, &func_value, function); // 1d. Load it as a value func_ptr = tilde_load_value(c, &func_value); // 1e. Calculate the function type func_type = tilde_get_func_prototype(c, prototype); } else { // 2a. Get the function declaration Decl *function_decl = declptr(expr->call_expr.func_ref); always_inline = function_decl->func_decl.attr_inline; // 2b. Set signature, function and function type prototype = function_decl->type->function.prototype; func = tilde_get_function(c, function_decl); assert(func); func_type = tilde_get_func_prototype(c, prototype); } TB_Reg arg_values[512]; unsigned arg_count = 0; Type **params = prototype->param_types; ABIArgInfo **abi_args = prototype->abi_args; unsigned param_count = vec_size(params); Expr **args = expr->call_expr.arguments; Expr **varargs = NULL; Expr *vararg_splat = NULL; if (prototype->variadic != VARIADIC_NONE) { if (expr->call_expr.splat_vararg) { vararg_splat = expr->call_expr.splat; } else { varargs = expr->call_expr.varargs; } } FunctionPrototype copy; if (prototype->variadic == VARIADIC_RAW) { if (varargs || vararg_splat) { assert(!vararg_splat); copy = *prototype; copy.varargs = NULL; foreach(Expr*, varargs) { vec_add(copy.varargs, type_flatten(val->type)); } copy.ret_abi_info = NULL; copy.ret_by_ref_abi_info = NULL; copy.abi_args = NULL; c_abi_func_create(©); prototype = © TB_DataType *params_type = NULL; tilde_update_prototype_abi(c, prototype, ¶ms_type); } } ABIArgInfo *ret_info = prototype->ret_abi_info; Type *call_return_type = prototype->abi_ret_type; // 5. In the case of an optional, the error is replacing the regular return abi. TB_Reg error_var = TB_NULL_REG; *result_value = (TBEValue){ .kind = TBE_VALUE, .reg = TB_NULL_REG }; // 6. Generate data for the return value. bool sret_return = false; switch (ret_info->kind) { case ABI_ARG_INDIRECT: // 6a. We can use the stored error var if there is no redirect. if (prototype->is_optional && c->opt_var && !ret_info->attributes.realign) { error_var = c->opt_var; arg_values[arg_count++] = error_var; break; } // 6b. Return true is indirect, in this case we allocate a local, using the desired alignment on the caller side. assert(ret_info->attributes.realign || ret_info->indirect.alignment == type_abi_alignment(call_return_type)); AlignSize alignment = ret_info->indirect.alignment; // If we have a target, then use it. if (target && alignment <= target->alignment) { assert(target->kind == TBE_ADDRESS); arg_values[arg_count++] = target->reg; sret_return = true; break; } value_set_address(result_value, tilde_emit_alloca(c, call_return_type, alignment), call_return_type, alignment); // 6c. Add the pointer to the list of arguments. arg_values[arg_count++] = result_value->reg; break; case ABI_ARG_EXPAND: case ABI_ARG_DIRECT_SPLIT_STRUCT: UNREACHABLE case ABI_ARG_DIRECT_PAIR: case ABI_ARG_IGNORE: case ABI_ARG_DIRECT_COERCE_INT: case ABI_ARG_DIRECT_COERCE: case ABI_ARG_DIRECT: case ABI_ARG_EXPAND_COERCE: break; } // 7. We might have an optional indirect return and a normal return. // In this case we need to add it by hand. TBEValue synthetic_return_param = { 0 }; if (prototype->ret_by_ref) { // 7b. Create the address to hold the return. Type *actual_return_type = type_lowering(prototype->ret_by_ref_type); value_set(&synthetic_return_param, tilde_emit_alloca(c, actual_return_type, 0), type_get_ptr(actual_return_type)); // 7c. Emit it as a parameter as a pointer (will implicitly add it to the value list) tilde_emit_parameter(c, arg_values, &arg_count, prototype->ret_by_ref_abi_info, &synthetic_return_param, synthetic_return_param.type); // 7d. Update the be_value to actually be an address. value_set_address_abi_aligned(&synthetic_return_param, synthetic_return_param.reg, actual_return_type); } // 8. Add all other arguments. for (unsigned i = 0; i < param_count; i++) { // 8a. Evaluate the expression. Expr *arg_expr = args[i]; Type *param = params[i]; ABIArgInfo *info = abi_args[i]; if (arg_expr) { tilde_emit_expr(c, &temp_value, arg_expr); } else { assert(prototype->variadic == VARIADIC_TYPED || prototype->variadic == VARIADIC_ANY); TODO // tilde_emit_vararg_parameter(c, &temp_value, param, info, varargs, vararg_splat); } // 8b. Emit the parameter according to ABI rules. tilde_emit_parameter(c, arg_values, &arg_count, info, &temp_value, param); } // 9. Typed varargs if (prototype->variadic == VARIADIC_RAW) { if (prototype->abi_varargs) { // 9. Emit varargs. unsigned index = 0; ABIArgInfo **abi_varargs = prototype->abi_varargs; foreach(Expr*, varargs) { tilde_emit_expr(c, &temp_value, val); ABIArgInfo *info = abi_varargs[index]; tilde_emit_parameter(c, arg_values, &arg_count, info, &temp_value, prototype->varargs[index]); index++; } } else { // 9. Emit varargs. foreach(Expr*, varargs) { tilde_emit_expr(c, &temp_value, val); REMINDER("Varargs should be expanded correctly"); arg_values[arg_count++] = tilde_load_value(c, &temp_value); } } } // 10. Create the actual call (remember to emit a loc, because we might have shifted loc emitting the params) EMIT_LOC(c, expr); int inline_flag = 0; if (expr->call_expr.attr_force_noinline) { inline_flag = -1; } else { inline_flag = expr->call_expr.attr_force_inline || always_inline ? 1 : 0; } tilde_emit_raw_call(c, result_value, prototype, func_type, func, func_ptr, arg_values, arg_count, inline_flag, error_var, sret_return, &synthetic_return_param); // Emit the current stack into the thread local or things will get messed up. if (c->debug.last_ptr) { tilde_store_to_ptr_raw_aligned(c, type_voidptr, c->debug.last_ptr, c->debug.stack_slot, type_alloca_alignment(type_voidptr)); } // 17i. The simple case here is where there is a normal return. // In this case be_value already holds the result return; } TBEValue tilde_emit_assign_expr(TildeContext *c, TBEValue *ref, Expr *expr, TB_Reg optional) { assert(ref->kind == TBE_ADDRESS || ref->kind == TBE_ADDRESS_OPTIONAL); assert(optional || !IS_OPTIONAL(expr)); // Special optimization of handling of optional if (expr->expr_kind == EXPR_OPTIONAL) { PUSH_OPT(); c->opt_var = TB_NULL_REG; c->catch_block = TB_NULL_REG; TBEValue result; // Emit the fault type. tilde_emit_expr(c, &result, expr->inner_expr); TB_Reg err_val = result.reg; // Store it in the optional tilde_store_to_ptr(c, optional, &result); // Set the result to an undef value value_set(&result, TB_NULL_REG, ref->type); POP_OPT(); // If we had a catch block outside then we want to jump to that exit. if (c->catch_block) tilde_emit_jump_to_optional_exit(c, err_val); // This return value will not be used. return result; } PUSH_OPT(); TB_Label assign_block = 0; TB_Label rejump_block = 0; if (IS_OPTIONAL(expr)) { assign_block = tb_basic_block_create(c->f); assert(optional); if (c->opt_var) { c->catch_block = rejump_block = tb_basic_block_create(c->f); } else { c->catch_block = assign_block; } c->opt_var = optional; } else { c->opt_var = 0; c->catch_block = 0; } TBEValue value; if (type_flat_is_vector(expr->type)) { tilde_emit_expr(c, &value, expr); tilde_store(c, ref, &value); } else if (expr_is_const_initializer(expr)) { TODO //llvm_emit_const_initialize_reference(c, ref, expr); value = *ref; } else if (expr_is_init_list(expr)) { TODO //llvm_emit_initialize_reference(c, ref, expr); value = *ref; } else { if (expr->expr_kind == EXPR_CALL) { tilde_emit_call_expr(c, &value, expr, ref); } else { tilde_emit_expr(c, &value, expr); } if (value.type != type_void) tilde_store(c, ref, &value); } if (optional) { tilde_store_to_ptr_raw(c, optional, tilde_get_zero(c, type_anyfault), type_anyfault); } POP_OPT(); if (assign_block) { tb_inst_goto(c->f, assign_block); if (rejump_block) { tilde_emit_block(c, rejump_block); TB_Reg error = tilde_load_abi_alignment(c, type_anyfault, optional); tilde_store_to_ptr_raw(c, c->opt_var, error, type_anyfault); tb_inst_goto(c->f, c->catch_block); } tilde_emit_block(c, assign_block); } return value; } static inline TB_Reg tilde_emit_add_int(TildeContext *c, Type *type, TB_Reg left, TB_Reg right, SourceSpan loc) { if (active_target.feature.trap_on_wrap) { REMINDER("Unable to trap on wrap"); TODO /*-- LLVMTypeRef type_to_use = llvm_get_type(c, type->canonical); LLVMValueRef args[2] = { left, right }; assert(type->canonical == type); LLVMValueRef add_res; if (type_is_unsigned(type)) { add_res = llvm_emit_call_intrinsic(c, intrinsic_id.uadd_overflow, &type_to_use, 1, args, 2); } else { add_res = llvm_emit_call_intrinsic(c, intrinsic_id.sadd_overflow, &type_to_use, 1, args, 2); } LLVMValueRef result = llvm_emit_extract_value(c, add_res, 0); LLVMValueRef ok = llvm_emit_extract_value(c, add_res, 1); llvm_emit_panic_on_true(c, ok, "Addition overflow", loc); return result; --*/ } return tb_inst_add(c->f, left, right, (TB_ArithmaticBehavior)0); } static inline TB_Reg tilde_emit_mult_int(TildeContext *c, Type *type, TB_Reg left, TB_Reg right, SourceSpan loc) { if (active_target.feature.trap_on_wrap) { TODO /* LLVMTypeRef type_to_use = llvm_get_type(c, type); LLVMValueRef args[2] = { left, right }; LLVMTypeRef types[2] = { type_to_use, type_to_use }; unsigned operation = type_is_integer_unsigned(type) ? intrinsic_id.umul_overflow : intrinsic_id.smul_overflow; LLVMValueRef call_res = llvm_emit_call_intrinsic(c, operation, types, 1, args, 2); LLVMValueRef val = llvm_emit_extract_value(c, call_res, 0); LLVMValueRef ok = llvm_emit_extract_value(c, call_res, 1); llvm_emit_panic_on_true(c, ok, "Integer multiplication overflow", loc); return val;*/ } return tb_inst_mul(c->f, left, right, (TB_ArithmaticBehavior)0); } void tilde_emit_int_comp_raw(TildeContext *c, TBEValue *result, Type *lhs_type, Type *rhs_type, TB_Reg lhs_value, TB_Reg rhs_value, BinaryOp binary_op) { bool lhs_signed, rhs_signed; Type *vector_type = type_vector_type(lhs_type); if (vector_type) { lhs_signed = type_is_signed(vector_type); rhs_signed = type_is_signed(type_vector_type(rhs_type)); } else { assert(type_is_integer_or_bool_kind(lhs_type)); lhs_signed = type_is_signed(lhs_type); rhs_signed = type_is_signed(rhs_type); } if (lhs_signed != rhs_signed) { // Swap sides if needed. if (!lhs_signed) { Type *temp = lhs_type; lhs_type = rhs_type; rhs_type = temp; lhs_signed = true; rhs_signed = false; TB_Reg temp_val = lhs_value; lhs_value = rhs_value; rhs_value = temp_val; switch (binary_op) { case BINARYOP_GE: binary_op = BINARYOP_LE; break; case BINARYOP_GT: binary_op = BINARYOP_LT; break; case BINARYOP_LE: binary_op = BINARYOP_GE; break; case BINARYOP_LT: binary_op = BINARYOP_GT; break; default: break; } } } if (!lhs_signed) { assert(lhs_signed == rhs_signed); // Right and left side are both unsigned. TB_Reg value; switch (binary_op) { case BINARYOP_EQ: value = tb_inst_cmp_eq(c->f, lhs_value, rhs_value); break; case BINARYOP_NE: value = tb_inst_cmp_ne(c->f, lhs_value, rhs_value); break; case BINARYOP_GE: value = tb_inst_cmp_ige(c->f, lhs_value, rhs_value, false); break; case BINARYOP_GT: value = tb_inst_cmp_igt(c->f, lhs_value, rhs_value, false); break; case BINARYOP_LE: value = tb_inst_cmp_ile(c->f, lhs_value, rhs_value, false); break; case BINARYOP_LT: value = tb_inst_cmp_ilt(c->f, lhs_value, rhs_value, false); break; default: UNREACHABLE } if (vector_type) { TODO // llvm_convert_vector_comparison(c, result, value, lhs_type, binary_op == BINARYOP_EQ); return; } value_set(result, value, type_bool); return; } // Left side is signed. TB_Reg comp_value; TB_Reg check_value; switch (binary_op) { case BINARYOP_EQ: comp_value = tb_inst_cmp_eq(c->f, lhs_value, rhs_value); break; case BINARYOP_NE: comp_value = tb_inst_cmp_ne(c->f, lhs_value, rhs_value); break; case BINARYOP_GE: comp_value = tb_inst_cmp_ige(c->f, lhs_value, rhs_value, true); break; case BINARYOP_GT: comp_value = tb_inst_cmp_igt(c->f, lhs_value, rhs_value, true); break; case BINARYOP_LE: comp_value = tb_inst_cmp_ile(c->f, lhs_value, rhs_value, true); break; case BINARYOP_LT: comp_value = tb_inst_cmp_ilt(c->f, lhs_value, rhs_value, true); break; default: UNREACHABLE } // If right side is also signed then this is fine. if (rhs_signed) { if (vector_type) { TODO // tilde_convert_vector_comparison(c, result, comp_value, lhs_type, binary_op == BINARYOP_EQ); return; } value_set(result, comp_value, type_bool); return; } // Otherwise, special handling for left side signed, right side unsigned. TB_Reg zero = tilde_get_zero(c, lhs_type); switch (binary_op) { case BINARYOP_EQ: // Only true if lhs >= 0 check_value = tb_inst_cmp_ige(c->f, lhs_value, zero, true); comp_value = tb_inst_and(c->f, check_value, comp_value); break; case BINARYOP_NE: // Always true if lhs < 0 check_value = tb_inst_cmp_ilt(c->f, lhs_value, zero, true); comp_value = tb_inst_and(c->f, check_value, comp_value); break; case BINARYOP_GE: // Only true if rhs >= 0 when regarded as a signed integer check_value = tb_inst_cmp_ige(c->f, rhs_value, zero, true); comp_value = tb_inst_and(c->f, check_value, comp_value); break; case BINARYOP_GT: // Only true if rhs >= 0 when regarded as a signed integer check_value = tb_inst_cmp_ige(c->f, rhs_value, zero, true); comp_value = tb_inst_and(c->f, check_value, comp_value); break; case BINARYOP_LE: // Always true if rhs < 0 when regarded as a signed integer check_value = tb_inst_cmp_ilt(c->f, rhs_value, zero, true); comp_value = tb_inst_or(c->f, check_value, comp_value); break; case BINARYOP_LT: // Always true if rhs < 0 when regarded as a signed integer check_value = tb_inst_cmp_ilt(c->f, rhs_value, zero, true); comp_value = tb_inst_or(c->f, check_value, comp_value); break; default: UNREACHABLE } if (vector_type) { TODO // tilde_convert_vector_comparison(c, result, comp_value, lhs_type, BINARYOP_EQ == binary_op); return; } value_set(result, comp_value, type_bool); } void tilde_emit_comp(TildeContext *c, TBEValue *result, TBEValue *lhs, TBEValue *rhs, BinaryOp binary_op) { assert(binary_op >= BINARYOP_GT && binary_op <= BINARYOP_EQ); value_rvalue(c, lhs); value_rvalue(c, rhs); if (type_is_integer_or_bool_kind(lhs->type)) { tilde_emit_int_comp_raw(c, result, lhs->type, rhs->type, lhs->reg, rhs->reg, binary_op); return; } if (type_is_pointer(lhs->type)) { TODO // tilde_emit_ptr_comparison(c, result, lhs, rhs, binary_op); return; } if (type_is_float(lhs->type)) { TODO // tilde_emit_float_comp(c, result, lhs, rhs, binary_op, NULL); return; } if (lhs->type->type_kind == TYPE_SUBARRAY) { TODO // tilde_emit_subarray_comp(c, result, lhs, rhs, binary_op); return; } if (lhs->type->type_kind == TYPE_VECTOR) { Type *type = type_vector_type(lhs->type); if (type_is_float(type)) { TODO // tilde_emit_float_comp(c, result, lhs, rhs, binary_op, lhs->type); } else { TODO // tilde_emit_int_comp_raw(c, result, lhs->type, rhs->type, lhs->reg, rhs->reg, binary_op); } return; } if (lhs->type->type_kind == TYPE_ARRAY) { TODO // tilde_emit_array_comp(c, result, lhs, rhs, binary_op); return; } TODO } void tilde_emit_binary(TildeContext *c, TBEValue *be_value, Expr *expr, TBEValue *lhs_loaded, BinaryOp binary_op) { // foo ?? bar if (binary_op == BINARYOP_ELSE) { TODO // llvm_emit_else(c, be_value, expr); return; } // foo || bar and foo && bar if (binary_op == BINARYOP_AND || binary_op == BINARYOP_OR) { TODO // llvm_emit_logical_and_or(c, be_value, expr, binary_op); return; } // Load if needed, otherwise use the already loaded. TBEValue lhs; if (lhs_loaded) { lhs = *lhs_loaded; } else { if (type_is_float(type_flatten(expr->type)) && (binary_op == BINARYOP_ADD || binary_op == BINARYOP_SUB)) { TODO // if (tilde_emit_fmuladd_maybe(c, be_value, expr, binary_op)) return; } tilde_emit_expr(c, &lhs, exprptr(expr->binary_expr.left)); } // We need the rvalue. value_rvalue(c, &lhs); // Evaluate rhs TBEValue rhs; tilde_emit_expr(c, &rhs, exprptr(expr->binary_expr.right)); value_rvalue(c, &rhs); EMIT_LOC(c, expr); // Comparison <=> if (binary_op >= BINARYOP_GT && binary_op <= BINARYOP_EQ) { tilde_emit_comp(c, be_value, &lhs, &rhs, binary_op); return; } Type *lhs_type = lhs.type; Type *rhs_type = rhs.type; Type *vector_type = lhs_type->type_kind == TYPE_VECTOR ? lhs_type->array.base : NULL; bool is_float = type_is_float(lhs_type) || (vector_type && type_is_float(vector_type)); TB_Reg val = TB_NULL_REG; TB_Reg lhs_value = lhs.reg; TB_Reg rhs_value = rhs.reg; switch (binary_op) { case BINARYOP_ERROR: UNREACHABLE case BINARYOP_MULT: if (is_float) { val = tb_inst_fmul(c->f, lhs_value, rhs_value); break; } val = tilde_emit_mult_int(c, lhs_type, lhs_value, rhs_value, expr->span); break; case BINARYOP_SUB: if (lhs_type->type_kind == TYPE_POINTER) { TODO /*--- if (lhs_type == rhs_type) { LLVMTypeRef int_type = llvm_get_type(c, type_isz); val = LLVMBuildSub(c->builder, LLVMBuildPtrToInt(c->builder, lhs_value, int_type, ""), LLVMBuildPtrToInt(c->builder, rhs_value, int_type, ""), ""); val = LLVMBuildExactSDiv(c->builder, val, llvm_const_int(c, type_isz, type_abi_alignment(lhs_type->pointer)), ""); break; } rhs_value = LLVMBuildNeg(c->builder, rhs_value, ""); val = llvm_emit_pointer_gep_raw(c, llvm_get_pointee_type(c, lhs_type), lhs_value, rhs_value); */ break; } if (is_float) { val = tb_inst_fsub(c->f, lhs_value, rhs_value); break; } TODO // val = tilde_emit_sub_int(c, lhs_type, lhs_value, rhs_value, expr->span); break; case BINARYOP_ADD: if (lhs_type->type_kind == TYPE_POINTER) { assert(type_is_integer(rhs_type)); TODO // val = llvm_emit_pointer_gep_raw(c, llvm_get_pointee_type(c, lhs_type), lhs_value, rhs_value); break; } if (is_float) { val = tb_inst_fadd(c->f, lhs_value, rhs_value); break; } val = tilde_emit_add_int(c, lhs_type, lhs_value, rhs_value, expr->span); break; case BINARYOP_DIV: TODO // tilde_emit_trap_zero(c, rhs_type, rhs_value, "Division by zero.", expr->span); if (is_float) { val = tb_inst_fdiv(c->f, lhs_value, rhs_value); break; } val = tb_inst_div(c->f, lhs_value, rhs_value, type_is_signed(lhs_type)); break; case BINARYOP_MOD: TODO // tilde_emit_trap_zero(c, rhs_type, rhs_value, "% by zero.", expr->span); if (type_is_float(lhs_type)) { TODO // val = LLVMBuildFRem(c->builder, lhs_value, rhs_value, "fmod"); break; } val = tb_inst_mod(c->f, lhs_value, rhs_value, type_is_signed(lhs_type)); break; case BINARYOP_SHR: TODO /*--- rhs_value = llvm_zext_trunc(c, rhs_value, LLVMTypeOf(lhs_value)); llvm_emit_trap_invalid_shift(c, rhs_value, lhs_type, "Shift amount out of range.", expr->span); val = type_is_unsigned(lhs_type) ? LLVMBuildLShr(c->builder, lhs_value, rhs_value, "lshr") : LLVMBuildAShr(c->builder, lhs_value, rhs_value, "ashr"); val = LLVMBuildFreeze(c->builder, val, ""); --*/ break; case BINARYOP_SHL: /*--- rhs_value = llvm_zext_trunc(c, rhs_value, LLVMTypeOf(lhs_value)); llvm_emit_trap_invalid_shift(c, rhs_value, lhs_type, "Shift amount out of range.", expr->span); val = LLVMBuildShl(c->builder, lhs_value, rhs_value, "shl"); val = LLVMBuildFreeze(c->builder, val, ""); --*/ break; case BINARYOP_BIT_AND: val = tb_inst_and(c->f, lhs_value, rhs_value); break; case BINARYOP_BIT_OR: val = tb_inst_or(c->f, lhs_value, rhs_value); break; case BINARYOP_BIT_XOR: val = tb_inst_xor(c->f, lhs_value, rhs_value); break; case BINARYOP_ELSE: case BINARYOP_EQ: case BINARYOP_NE: case BINARYOP_GE: case BINARYOP_GT: case BINARYOP_LE: case BINARYOP_LT: case BINARYOP_AND: case BINARYOP_OR: case BINARYOP_ASSIGN: case BINARYOP_MULT_ASSIGN: case BINARYOP_ADD_ASSIGN: case BINARYOP_SUB_ASSIGN: case BINARYOP_DIV_ASSIGN: case BINARYOP_MOD_ASSIGN: case BINARYOP_BIT_AND_ASSIGN: case BINARYOP_BIT_OR_ASSIGN: case BINARYOP_BIT_XOR_ASSIGN: case BINARYOP_SHR_ASSIGN: case BINARYOP_SHL_ASSIGN: // Handled elsewhere. UNREACHABLE } assert(val); value_set(be_value, val, expr->type); } static void tilde_emit_binary_expr(TildeContext *c, TBEValue *be_value, Expr *expr) { BinaryOp binary_op = expr->binary_expr.operator; if (binary_op >= BINARYOP_ASSIGN && expr_is_vector_index(exprptr(expr->binary_expr.left))) { TODO // llvm_emit_vector_assign_expr(c, be_value, expr); return; } if (binary_op > BINARYOP_ASSIGN) { BinaryOp base_op = binaryop_assign_base_op(binary_op); assert(base_op != BINARYOP_ERROR); TBEValue addr; tilde_emit_expr(c, &addr, exprptr(expr->binary_expr.left)); value_addr(c, &addr); tilde_emit_binary(c, be_value, expr, &addr, base_op); tilde_store(c, &addr, be_value); return; } if (binary_op == BINARYOP_ASSIGN) { Expr *left = exprptr(expr->binary_expr.left); tilde_emit_expr(c, be_value, left); assert(value_is_addr(be_value)); TB_Reg optional_ref = TB_NULL_REG; if (left->expr_kind == EXPR_IDENTIFIER) { TODO /*--- optional_ref = decl_optional_ref(left->identifier_expr.decl); be_value->kind = BE_ADDRESS;*/ } *be_value = tilde_emit_assign_expr(c, be_value, exprptr(expr->binary_expr.right), optional_ref); return; } tilde_emit_binary(c, be_value, expr, NULL, binary_op); } void tilde_emit_expr(TildeContext *c, TBEValue *value, Expr *expr) { EMIT_LOC(c, expr); switch (expr->expr_kind) { case NON_RUNTIME_EXPR: case EXPR_COND: case EXPR_CT_ARG: case EXPR_ASM: case EXPR_VASPLAT: case EXPR_CT_CHECKS: case EXPR_BUILTIN: UNREACHABLE case EXPR_CONST: tilde_emit_const_expr(c, value, expr); return; case EXPR_BINARY: tilde_emit_binary_expr(c, value, expr); return; case EXPR_NOP: value_set(value, TB_NULL_REG, type_void); return; case EXPR_IDENTIFIER: value_set_decl(c, value, expr->identifier_expr.decl); return; case EXPR_CALL: tilde_emit_call_expr(c, value, expr, NULL); return; default: break; } TODO } void tilde_emit_cast(TildeContext *c, CastKind cast_kind, Expr *expr, TBEValue *value, Type *to_type, Type *from_type) { Type *to_type_original = to_type; to_type = type_flatten(to_type); from_type = type_flatten(from_type); switch (cast_kind) { case CAST_NUMVEC: TODO // llvm_emit_num_to_vec_cast(c, value, to_type, from_type); return; case CAST_BOOLVECINT: TODO // llvm_emit_bool_to_intvec_cast(c, value, to_type, from_type); return; case CAST_ARRVEC: TODO // llvm_emit_array_to_vector_cast(c, value, to_type, from_type); return; case CAST_PTRANY: { value_rvalue(c, value); TODO /*-- LLVMValueRef pointer = llvm_emit_bitcast(c, value->value, type_voidptr); BEValue typeid; llvm_emit_typeid(c, &typeid, from_type->pointer); llvm_value_aggregate_two(c, value, to_type, pointer, typeid.value); return;--*/ } case CAST_BSARRY: TODO /*-- llvm_value_addr(c, value); llvm_value_bitcast(c, value, to_type); llvm_value_rvalue(c, value);--*/ return; /* case CAST_BSINT: llvm_value_addr(c, value); llvm_value_bitcast(c, value, to_type); llvm_value_rvalue(c, value); return; case CAST_EUINT: case CAST_ERINT: to_type = type_lowering(to_type); from_type = type_lowering(from_type); llvm_value_rvalue(c, value); if (type_convert_will_trunc(to_type, from_type)) { value->value = LLVMBuildTrunc(c->builder, value->value, llvm_get_type(c, to_type), "errinttrunc"); } else { value->value = type_is_signed(to_type) ? LLVMBuildSExt(c->builder, value->value, llvm_get_type(c, to_type), "errsiext") : LLVMBuildZExt(c->builder, value->value, llvm_get_type(c, to_type), "erruiext"); } break; case CAST_ANYPTR: llvm_emit_any_pointer(c, value, value); if (llvm_value_is_addr(value)) { value->value = LLVMBuildBitCast(c->builder, value->value, llvm_get_ptr_type(c, to_type), ""); } else { value->value = LLVMBuildBitCast(c->builder, value->value, llvm_get_type(c, to_type), ""); } break; case CAST_XIERR: to_type = type_lowering(to_type); llvm_value_rvalue(c, value); value->value = llvm_zext_trunc(c, value->value, llvm_get_type(c, to_type)); break; case CAST_ERROR: UNREACHABLE case CAST_STRPTR: case CAST_PTRPTR: llvm_value_rvalue(c, value); value->value = LLVMBuildPointerCast(c->builder, value->value, llvm_get_type(c, to_type), "ptrptr"); break; case CAST_PTRXI: llvm_value_rvalue(c, value); value->value = LLVMBuildPtrToInt(c->builder, value->value, llvm_get_type(c, to_type), "ptrxi"); break; case CAST_APTSA: llvm_emit_arr_to_subarray_cast(c, value, to_type); break; case CAST_SASA: assert(type_is_pointer(value->type->array.base)); llvm_value_addr(c, value); llvm_value_bitcast(c, value, to_type); break; case CAST_SAPTR: llvm_emit_subarray_pointer(c, value, value); if (value->type != to_type) { if (llvm_value_is_addr(value)) { value->value = LLVMBuildPointerCast(c->builder, value->value, llvm_get_ptr_type(c, to_type), "saptr"); } else { value->value = LLVMBuildPointerCast(c->builder, value->value, llvm_get_ptr_type(c, to_type), "saptr"); } } break; case CAST_EREU: // This is a no op. assert(type_lowering(to_type) == type_lowering(from_type)); break; case CAST_VECARR: llvm_emit_vector_to_array_cast(c, value, to_type, from_type); break; case CAST_EUER: TODO // gencontext_emit_value_bitcast(c, value->value, to_type, from_type); case CAST_ERBOOL: case CAST_EUBOOL: { BEValue zero; llvm_value_set_int(c, &zero, type_anyfault, 0); llvm_emit_int_comp(c, value, value, &zero, BINARYOP_NE); break; } case CAST_PTRBOOL: llvm_value_rvalue(c, value); value->value = LLVMBuildIsNotNull(c->builder, value->value, "ptrbool"); value->kind = BE_BOOLEAN; break; case CAST_BOOLINT: llvm_value_rvalue(c, value); value->value = LLVMBuildZExt(c->builder, value->value, llvm_get_type(c, to_type), "boolsi"); value->kind = BE_VALUE; break; case CAST_FPBOOL: llvm_value_rvalue(c, value); value->value = LLVMBuildFCmp(c->builder, LLVMRealUNE, value->value, llvm_get_zero(c, from_type), "fpbool"); value->kind = BE_BOOLEAN; break; case CAST_BOOLBOOL: value->value = LLVMBuildTrunc(c->builder, value->value, c->bool_type, "boolbool"); value->kind = BE_BOOLEAN; break; case CAST_BOOLFP: llvm_value_rvalue(c, value); value->value = LLVMBuildUIToFP(c->builder, value->value, llvm_get_type(c, to_type), "boolfp"); value->kind = BE_VALUE; break; case CAST_INTBOOL: llvm_value_rvalue(c, value); value->value = LLVMBuildICmp(c->builder, LLVMIntNE, value->value, llvm_get_zero(c, from_type), "intbool"); value->kind = type_kind_is_any_vector(value->type->type_kind) ? BE_BOOLVECTOR : BE_BOOLEAN; break; case CAST_FPFP: llvm_value_rvalue(c, value); value->value = type_convert_will_trunc(to_type, from_type) ? LLVMBuildFPTrunc(c->builder, value->value, llvm_get_type(c, to_type), "fpfptrunc") : LLVMBuildFPExt(c->builder, value->value, llvm_get_type(c, to_type), "fpfpext"); break; case CAST_FPSI: llvm_value_rvalue(c, value); value->value = LLVMBuildFPToSI(c->builder, value->value, llvm_get_type(c, to_type), "fpsi"); break; case CAST_FPUI: llvm_value_rvalue(c, value); value->value = LLVMBuildFPToUI(c->builder, value->value, llvm_get_type(c, to_type), "fpui"); break; case CAST_SISI: llvm_value_rvalue(c, value); value->value = type_convert_will_trunc(to_type, from_type) ? LLVMBuildTrunc(c->builder, value->value, llvm_get_type(c, to_type), "sisitrunc") : LLVMBuildSExt(c->builder, value->value, llvm_get_type(c, to_type), "sisiext"); break; case CAST_SIUI: llvm_value_rvalue(c, value); value->value = type_convert_will_trunc(to_type, from_type) ? LLVMBuildTrunc(c->builder, value->value, llvm_get_type(c, to_type), "siuitrunc") : LLVMBuildSExt(c->builder, value->value, llvm_get_type(c, to_type), "siuiext"); break; case CAST_SIFP: llvm_value_rvalue(c, value); value->value = LLVMBuildSIToFP(c->builder, value->value, llvm_get_type(c, to_type), "sifp"); break; case CAST_XIPTR: llvm_value_rvalue(c, value); value->value = LLVMBuildIntToPtr(c->builder, value->value, llvm_get_type(c, to_type), "xiptr"); break; case CAST_UISI: llvm_value_rvalue(c, value); value->value = type_convert_will_trunc(to_type, from_type) ? LLVMBuildTrunc(c->builder, value->value, llvm_get_type(c, to_type), "uisitrunc") : LLVMBuildZExt(c->builder, value->value, llvm_get_type(c, to_type), "uisiext"); break; case CAST_UIUI: llvm_value_rvalue(c, value); value->value = llvm_zext_trunc(c, value->value, llvm_get_type(c, to_type)); break; case CAST_UIFP: llvm_value_rvalue(c, value); value->value = LLVMBuildUIToFP(c->builder, value->value, llvm_get_type(c, to_type), "uifp"); break; case CAST_ENUMLOW: llvm_value_rvalue(c, value); break; case CAST_STST: llvm_value_addr(c, value); value->value = LLVMBuildBitCast(c->builder, value->value, llvm_get_ptr_type(c, to_type), ""); value->type = to_type; return; case CAST_INTENUM: if (active_target.feature.safe_mode && c->builder != c->global_builder) { llvm_value_rvalue(c, value); BEValue check; Decl *decl = to_type_original->canonical->decl; unsigned max = vec_size(decl->enums.values); if (type_is_signed(value->type)) { scratch_buffer_clear(); scratch_buffer_printf("Conversion to enum '%s' failed - tried to convert a negative value.", decl->name); llvm_emit_int_comp_zero(c, &check, value, BINARYOP_LT); llvm_emit_panic_on_true(c, check.value, scratch_buffer_to_string(), expr->span); } scratch_buffer_clear(); scratch_buffer_printf("Conversion to enum '%s' failed - the value was greater than %u.", decl->name, max - 1); LLVMValueRef val = llvm_const_int(c, value->type, max); llvm_emit_int_comp_raw(c, &check, value->type, value->type, value->value, val, BINARYOP_GE); llvm_emit_panic_on_true(c, check.value,scratch_buffer_to_string(), expr->span); } return; case CAST_SABOOL: llvm_value_fold_optional(c, value); if (llvm_value_is_addr(value)) { value->value = llvm_emit_struct_gep_raw(c, value->value, llvm_get_type(c, value->type), 1, value->alignment, &value->alignment); } else { value->value = llvm_emit_extract_value(c, value->value, 1); } value->type = type_usz->canonical; llvm_value_rvalue(c, value); llvm_emit_int_comp_zero(c, value, value, BINARYOP_NE); break;*/ default: TODO } value->type = to_type; }