From a31a749c42505e53308c0f2426db283ea130e776 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 19 Jan 2022 12:26:30 +0200 Subject: stage1: add f80 type --- src/stage1/codegen.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/stage1/codegen.cpp') diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index f9f37c2eb4..efb33e8bdc 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -7692,6 +7692,12 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f32); case 64: return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f64); + case 80: { + uint64_t buf[2]; + memcpy(&buf, &const_val->data.x_f80, 16); + LLVMValueRef as_int = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, buf); + return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry)); + } case 128: { uint64_t buf[2]; @@ -8911,6 +8917,13 @@ static void define_builtin_types(CodeGen *g) { add_fp_entry(g, "f64", 64, LLVMDoubleType(), &g->builtin_types.entry_f64); add_fp_entry(g, "f128", 128, LLVMFP128Type(), &g->builtin_types.entry_f128); + if (target_has_f80(g->zig_target)) { + add_fp_entry(g, "f80", 80, LLVMX86FP80Type(), &g->builtin_types.entry_f80); + } else { + // use f128 for correct size and alignment + add_fp_entry(g, "f80", 128, LLVMFP128Type(), &g->builtin_types.entry_f80); + } + switch (g->zig_target->arch) { case ZigLLVM_x86: case ZigLLVM_x86_64: -- cgit v1.2.3 From 8e9fd042b8a56fc4bb8eeae3878b095af96eb1a6 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 19 Jan 2022 21:16:23 +0200 Subject: stage1: emit calls to compiler-rt for f80 on unsupported targets --- src/stage1/codegen.cpp | 240 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 236 insertions(+), 4 deletions(-) (limited to 'src/stage1/codegen.cpp') diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index efb33e8bdc..f8e12e1d78 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -1598,6 +1598,81 @@ static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType * return nullptr; } + +static LLVMValueRef gen_soft_f80_widen_or_shorten(CodeGen *g, ZigType *actual_type, + ZigType *wanted_type, LLVMValueRef expr_val) +{ + ZigType *scalar_actual_type = (actual_type->id == ZigTypeIdVector) ? + actual_type->data.vector.elem_type : actual_type; + ZigType *scalar_wanted_type = (wanted_type->id == ZigTypeIdVector) ? + wanted_type->data.vector.elem_type : wanted_type; + uint64_t actual_bits = scalar_actual_type->data.floating.bit_count; + uint64_t wanted_bits = scalar_wanted_type->data.floating.bit_count; + + + LLVMTypeRef param_type; + LLVMTypeRef return_type; + const char *func_name; + + if (actual_bits == wanted_bits) { + return expr_val; + } else if (actual_bits == 80) { + param_type = g->builtin_types.entry_f80->llvm_type; + switch (wanted_bits) { + case 16: + return_type = g->builtin_types.entry_f16->llvm_type; + func_name = "__truncxfhf2"; + break; + case 32: + return_type = g->builtin_types.entry_f32->llvm_type; + func_name = "__truncxfff2"; + break; + case 64: + return_type = g->builtin_types.entry_f64->llvm_type; + func_name = "__truncxfdf2"; + break; + case 128: + return_type = g->builtin_types.entry_f128->llvm_type; + func_name = "__extendxftf2"; + break; + default: + zig_unreachable(); + } + } else if (wanted_bits == 80) { + return_type = g->builtin_types.entry_f80->llvm_type; + switch (actual_bits) { + case 16: + param_type = g->builtin_types.entry_f16->llvm_type; + func_name = "__extendhfxf2"; + break; + case 32: + param_type = g->builtin_types.entry_f32->llvm_type; + func_name = "__extendffxf2"; + break; + case 64: + param_type = g->builtin_types.entry_f64->llvm_type; + func_name = "__extenddfxf2"; + break; + case 128: + param_type = g->builtin_types.entry_f128->llvm_type; + func_name = "__trunctfxf2"; + break; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } + + LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, func_name); + if (func_ref == nullptr) { + LLVMTypeRef fn_type = LLVMFunctionType(return_type, ¶m_type, 1, false); + func_ref = LLVMAddFunction(g->module, func_name, fn_type); + } + + return LLVMBuildCall(g->builder, func_ref, &expr_val, 1, ""); +} + static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, ZigType *actual_type, ZigType *wanted_type, LLVMValueRef expr_val) { @@ -1612,6 +1687,13 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z uint64_t actual_bits; uint64_t wanted_bits; if (scalar_actual_type->id == ZigTypeIdFloat) { + + if ((scalar_actual_type == g->builtin_types.entry_f80 + || scalar_wanted_type == g->builtin_types.entry_f80) + && !target_has_f80(g->zig_target)) + { + return gen_soft_f80_widen_or_shorten(g, actual_type, wanted_type, expr_val); + } actual_bits = scalar_actual_type->data.floating.bit_count; wanted_bits = scalar_wanted_type->data.floating.bit_count; } else if (scalar_actual_type->id == ZigTypeIdInt) { @@ -3142,6 +3224,142 @@ static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type } } +static LLVMValueRef get_soft_f80_bin_op_func(CodeGen *g, const char *name, int param_count, LLVMTypeRef return_type) { + LLVMValueRef existing_llvm_fn = LLVMGetNamedFunction(g->module, name); + if (existing_llvm_fn != nullptr) return existing_llvm_fn; + + LLVMTypeRef float_type_ref = g->builtin_types.entry_f80->llvm_type; + LLVMTypeRef param_types[2] = { float_type_ref, float_type_ref }; + LLVMTypeRef fn_type = LLVMFunctionType(return_type, param_types, param_count, false); + return LLVMAddFunction(g->module, name, fn_type); +} + +static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, + Stage1AirInstBinOp *bin_op_instruction) +{ + // TODO support vectors + IrBinOp op_id = bin_op_instruction->op_id; + Stage1AirInst *op1 = bin_op_instruction->op1; + Stage1AirInst *op2 = bin_op_instruction->op2; + + LLVMValueRef op1_value = ir_llvm_value(g, op1); + LLVMValueRef op2_value = ir_llvm_value(g, op2); + + bool div_exact_safety_check = false; + LLVMTypeRef return_type = g->builtin_types.entry_f80->llvm_type; + int param_count = 2; + const char *func_name; + switch (op_id) { + case IrBinOpInvalid: + case IrBinOpArrayCat: + case IrBinOpArrayMult: + case IrBinOpRemUnspecified: + case IrBinOpBitShiftLeftLossy: + case IrBinOpBitShiftLeftExact: + case IrBinOpBitShiftRightLossy: + case IrBinOpBitShiftRightExact: + case IrBinOpBoolOr: + case IrBinOpBoolAnd: + case IrBinOpMultWrap: + case IrBinOpAddWrap: + case IrBinOpSubWrap: + case IrBinOpBinOr: + case IrBinOpBinXor: + case IrBinOpBinAnd: + case IrBinOpAddSat: + case IrBinOpSubSat: + case IrBinOpMultSat: + case IrBinOpShlSat: + zig_unreachable(); + case IrBinOpCmpEq: + return_type = g->builtin_types.entry_i32->llvm_type; + func_name = "__eqxf2"; + break; + case IrBinOpCmpNotEq: + return_type = g->builtin_types.entry_i32->llvm_type; + func_name = "__nexf2"; + break; + case IrBinOpCmpLessOrEq: + case IrBinOpCmpLessThan: + return_type = g->builtin_types.entry_i32->llvm_type; + func_name = "__lexf2"; + break; + case IrBinOpCmpGreaterOrEq: + case IrBinOpCmpGreaterThan: + return_type = g->builtin_types.entry_i32->llvm_type; + func_name = "__gexf2"; + break; + case IrBinOpMaximum: + func_name = "__fmaxx"; + break; + case IrBinOpMinimum: + func_name = "__fminx"; + break; + case IrBinOpMult: + func_name = "__mulxf3"; + break; + case IrBinOpAdd: + func_name = "__addxf3"; + break; + case IrBinOpSub: + func_name = "__subxf3"; + break; + case IrBinOpDivUnspecified: + func_name = "__divxf3"; + break; + case IrBinOpDivExact: + func_name = "__divxf3"; + div_exact_safety_check = bin_op_instruction->safety_check_on && + ir_want_runtime_safety(g, &bin_op_instruction->base); + break; + case IrBinOpDivTrunc: + param_count = 1; + func_name = "__truncx"; + break; + case IrBinOpDivFloor: + param_count = 1; + func_name = "__floorx"; + break; + case IrBinOpRemRem: + param_count = 1; + func_name = "__remx"; + break; + case IrBinOpRemMod: + param_count = 1; + func_name = "__modx"; + break; + default: + zig_unreachable(); + } + + LLVMValueRef func_ref = get_soft_f80_bin_op_func(g, func_name, param_count, return_type); + + LLVMValueRef params[2] = {op1_value, op2_value}; + LLVMValueRef result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); + + if (div_exact_safety_check) { + // Safety check: a / b == floor(a / b) + func_ref = get_soft_f80_bin_op_func(g, "__floorx", 1, return_type); + LLVMValueRef floored = LLVMBuildCall(g->builder, func_ref, &result, 1, ""); + + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); + + LLVMValueRef params[2] = {result, floored}; + func_ref = get_soft_f80_bin_op_func(g, "__eqxf2", 2, g->builtin_types.entry_i32->llvm_type); + LLVMValueRef ok_bit = LLVMBuildCall(g->builder, func_ref, params, 2, ""); + + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdExactDivisionRemainder); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + + return result; +} + static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable, Stage1AirInstBinOp *bin_op_instruction) { @@ -3151,6 +3369,10 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable, ZigType *operand_type = op1->value->type; ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; + if (scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) { + return ir_render_soft_f80_bin_op(g, executable, bin_op_instruction); + } + bool want_runtime_safety = bin_op_instruction->safety_check_on && ir_want_runtime_safety(g, &bin_op_instruction->base); @@ -3158,7 +3380,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable, LLVMValueRef op1_value = ir_llvm_value(g, op1); LLVMValueRef op2_value = ir_llvm_value(g, op2); - switch (op_id) { case IrBinOpInvalid: case IrBinOpArrayCat: @@ -5927,7 +6148,7 @@ static LLVMValueRef ir_render_prefetch(CodeGen *g, Stage1Air *executable, Stage1 static_assert(PrefetchCacheInstruction == 0, ""); static_assert(PrefetchCacheData == 1, ""); assert(instruction->cache == PrefetchCacheData || instruction->cache == PrefetchCacheInstruction); - + // LLVM fails during codegen of instruction cache prefetchs for these architectures. // This is an LLVM bug as the prefetch intrinsic should be a noop if not supported by the target. // To work around this, simply don't emit llvm.prefetch in this case. @@ -8920,8 +9141,19 @@ static void define_builtin_types(CodeGen *g) { if (target_has_f80(g->zig_target)) { add_fp_entry(g, "f80", 80, LLVMX86FP80Type(), &g->builtin_types.entry_f80); } else { - // use f128 for correct size and alignment - add_fp_entry(g, "f80", 128, LLVMFP128Type(), &g->builtin_types.entry_f80); + ZigType *entry = new_type_table_entry(ZigTypeIdFloat); + entry->llvm_type = get_int_type(g, false, 128)->llvm_type; + entry->size_in_bits = 8 * LLVMStoreSizeOfType(g->target_data_ref, entry->llvm_type); + entry->abi_size = LLVMABISizeOfType(g->target_data_ref, entry->llvm_type); + entry->abi_align = 16; + buf_init_from_str(&entry->name, "f80"); + entry->data.floating.bit_count = 80; + + entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), + entry->size_in_bits, ZigLLVMEncoding_DW_ATE_unsigned()); + + g->builtin_types.entry_f80 = entry; + g->primitive_type_table.put(&entry->name, entry); } switch (g->zig_target->arch) { -- cgit v1.2.3 From 0f3bd2afa320252d4f0ece627917feaade734064 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 19 Jan 2022 22:39:14 +0200 Subject: stage1: handle compiler-rt calls on vectors of f80 --- src/stage1/codegen.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 9 deletions(-) (limited to 'src/stage1/codegen.cpp') diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index f8e12e1d78..548abebc6f 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -3237,10 +3237,10 @@ static LLVMValueRef get_soft_f80_bin_op_func(CodeGen *g, const char *name, int p static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, Stage1AirInstBinOp *bin_op_instruction) { - // TODO support vectors IrBinOp op_id = bin_op_instruction->op_id; Stage1AirInst *op1 = bin_op_instruction->op1; Stage1AirInst *op2 = bin_op_instruction->op2; + uint32_t vector_len = op1->value->type->id == ZigTypeIdVector ? op1->value->type->data.vector.len : 0; LLVMValueRef op1_value = ir_llvm_value(g, op1); LLVMValueRef op2_value = ir_llvm_value(g, op2); @@ -3334,21 +3334,63 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, LLVMValueRef func_ref = get_soft_f80_bin_op_func(g, func_name, param_count, return_type); - LLVMValueRef params[2] = {op1_value, op2_value}; - LLVMValueRef result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); + LLVMValueRef result; + if (vector_len == 0) { + LLVMValueRef params[2] = {op1_value, op2_value}; + result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); + } else { + result = build_alloca(g, op1->value->type, "", 0); + } + + LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; + for (uint32_t i = 0; i < vector_len; i++) { + LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); + LLVMValueRef params[2] = { + LLVMBuildExtractElement(g->builder, op1_value, index_value, ""), + LLVMBuildExtractElement(g->builder, op2_value, index_value, ""), + }; + LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); + LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), + call_result, index_value, ""); + } if (div_exact_safety_check) { // Safety check: a / b == floor(a / b) - func_ref = get_soft_f80_bin_op_func(g, "__floorx", 1, return_type); - LLVMValueRef floored = LLVMBuildCall(g->builder, func_ref, &result, 1, ""); + LLVMValueRef floor_func = get_soft_f80_bin_op_func(g, "__floorx", 1, return_type); + LLVMValueRef eq_func = get_soft_f80_bin_op_func(g, "__eqxf2", 2, g->builtin_types.entry_i32->llvm_type); + + LLVMValueRef ok_bit; + if (vector_len == 0) { + LLVMValueRef floored = LLVMBuildCall(g->builder, floor_func, &result, 1, ""); + + LLVMValueRef params[2] = {result, floored}; + ok_bit = LLVMBuildCall(g->builder, eq_func, params, 2, ""); + } else { + ZigType *bool_vec_ty = get_vector_type(g, vector_len, g->builtin_types.entry_bool); + ok_bit = build_alloca(g, bool_vec_ty, "", 0); + } + + for (uint32_t i = 0; i < vector_len; i++) { + LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); + LLVMValueRef div_res = LLVMBuildExtractElement(g->builder, + LLVMBuildLoad(g->builder, result, ""), index_value, ""); + LLVMValueRef params[2] = { + div_res, + LLVMBuildCall(g->builder, floor_func, &div_res, 1, ""), + }; + LLVMValueRef cmp_res = LLVMBuildCall(g->builder, eq_func, params, 2, ""); + cmp_res = LLVMBuildTrunc(g->builder, cmp_res, g->builtin_types.entry_bool->llvm_type, ""); + LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, ok_bit, ""), + cmp_res, index_value, ""); + } + + if (vector_len != 0) { + ok_bit = ZigLLVMBuildAndReduce(g->builder, LLVMBuildLoad(g->builder, ok_bit, "")); + } LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); - LLVMValueRef params[2] = {result, floored}; - func_ref = get_soft_f80_bin_op_func(g, "__eqxf2", 2, g->builtin_types.entry_i32->llvm_type); - LLVMValueRef ok_bit = LLVMBuildCall(g->builder, func_ref, params, 2, ""); - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); LLVMPositionBuilderAtEnd(g->builder, fail_block); @@ -3357,6 +3399,9 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, LLVMPositionBuilderAtEnd(g->builder, ok_block); } + if (vector_len != 0) { + result = LLVMBuildLoad(g->builder, result, ""); + } return result; } -- cgit v1.2.3 From f8b204bb189125e85a7f14f08c4bab60a462e76e Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 19 Jan 2022 23:05:35 +0200 Subject: stage1: call compiler-rt for math builtins on f80 on unsupported targets --- src/stage1/codegen.cpp | 135 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) (limited to 'src/stage1/codegen.cpp') diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 548abebc6f..2051d39a7c 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -6888,13 +6888,148 @@ static LLVMValueRef ir_render_atomic_store(CodeGen *g, Stage1Air *executable, return nullptr; } +static LLVMValueRef ir_render_soft_f80_float_op(CodeGen *g, Stage1Air *executable, Stage1AirInstFloatOp *instruction) { + ZigType *op_type = instruction->operand->value->type; + uint32_t vector_len = op_type->id == ZigTypeIdVector ? op_type->data.vector.len : 0; + + const char *func_name; + switch (instruction->fn_id) { + case BuiltinFnIdSqrt: + func_name = "__sqrt"; + break; + case BuiltinFnIdSin: + func_name = "__sinx"; + break; + case BuiltinFnIdCos: + func_name = "__cosx"; + break; + case BuiltinFnIdExp: + func_name = "__expx"; + break; + case BuiltinFnIdExp2: + func_name = "__exp2x"; + break; + case BuiltinFnIdLog: + func_name = "__logx"; + break; + case BuiltinFnIdLog2: + func_name = "__log2x"; + break; + case BuiltinFnIdLog10: + func_name = "__log10x"; + break; + case BuiltinFnIdFabs: + func_name = "__fabsx"; + break; + case BuiltinFnIdFloor: + func_name = "__floorx"; + break; + case BuiltinFnIdCeil: + func_name = "__ceilx"; + break; + case BuiltinFnIdTrunc: + func_name = "__truncx"; + break; + case BuiltinFnIdNearbyInt: + func_name = "__nearbyintx"; + break; + case BuiltinFnIdRound: + func_name = "__roundx"; + break; + default: + zig_unreachable(); + } + + + LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, func_name); + if (func_ref == nullptr) { + LLVMTypeRef f80_ref = g->builtin_types.entry_f80->llvm_type; + LLVMTypeRef fn_type = LLVMFunctionType(f80_ref, &f80_ref, 1, false); + func_ref = LLVMAddFunction(g->module, func_name, fn_type); + } + + LLVMValueRef operand = ir_llvm_value(g, instruction->operand); + LLVMValueRef result; + if (vector_len == 0) { + result = LLVMBuildCall(g->builder, func_ref, &operand, 1, ""); + } else { + result = build_alloca(g, instruction->operand->value->type, "", 0); + } + + LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; + for (uint32_t i = 0; i < vector_len; i++) { + LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); + LLVMValueRef param = LLVMBuildExtractElement(g->builder, operand, index_value, ""); + LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, ¶m, 1, ""); + LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), + call_result, index_value, ""); + } + if (vector_len != 0) { + result = LLVMBuildLoad(g->builder, result, ""); + } + return result; +} + static LLVMValueRef ir_render_float_op(CodeGen *g, Stage1Air *executable, Stage1AirInstFloatOp *instruction) { + ZigType *op_type = instruction->operand->value->type; + op_type = op_type->id == ZigTypeIdVector ? op_type->data.vector.elem_type : op_type; + if (op_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) { + return ir_render_soft_f80_float_op(g, executable, instruction); + } LLVMValueRef operand = ir_llvm_value(g, instruction->operand); LLVMValueRef fn_val = get_float_fn(g, instruction->base.value->type, ZigLLVMFnIdFloatOp, instruction->fn_id); return LLVMBuildCall(g->builder, fn_val, &operand, 1, ""); } +static LLVMValueRef ir_render_soft_f80_mul_add(CodeGen *g, Stage1Air *executable, Stage1AirInstMulAdd *instruction) { + ZigType *op_type = instruction->op1->value->type; + uint32_t vector_len = op_type->id == ZigTypeIdVector ? op_type->data.vector.len : 0; + + const char *func_name = "__fmax"; + LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, func_name); + if (func_ref == nullptr) { + LLVMTypeRef f80_ref = g->builtin_types.entry_f80->llvm_type; + LLVMTypeRef params[3] = { f80_ref, f80_ref, f80_ref }; + LLVMTypeRef fn_type = LLVMFunctionType(f80_ref, params, 3, false); + func_ref = LLVMAddFunction(g->module, func_name, fn_type); + } + + LLVMValueRef op1 = ir_llvm_value(g, instruction->op1); + LLVMValueRef op2 = ir_llvm_value(g, instruction->op2); + LLVMValueRef op3 = ir_llvm_value(g, instruction->op3); + LLVMValueRef result; + if (vector_len == 0) { + LLVMValueRef params[3] = { op1, op2, op3 }; + result = LLVMBuildCall(g->builder, func_ref, params, 3, ""); + } else { + result = build_alloca(g, instruction->op1->value->type, "", 0); + } + + LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; + for (uint32_t i = 0; i < vector_len; i++) { + LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); + + LLVMValueRef params[3] = { + LLVMBuildExtractElement(g->builder, op1, index_value, ""), + LLVMBuildExtractElement(g->builder, op2, index_value, ""), + LLVMBuildExtractElement(g->builder, op3, index_value, ""), + }; + LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, 3, ""); + LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), + call_result, index_value, ""); + } + if (vector_len != 0) { + result = LLVMBuildLoad(g->builder, result, ""); + } + return result; +} + static LLVMValueRef ir_render_mul_add(CodeGen *g, Stage1Air *executable, Stage1AirInstMulAdd *instruction) { + ZigType *op_type = instruction->op1->value->type; + op_type = op_type->id == ZigTypeIdVector ? op_type->data.vector.elem_type : op_type; + if (op_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) { + return ir_render_soft_f80_mul_add(g, executable, instruction); + } LLVMValueRef op1 = ir_llvm_value(g, instruction->op1); LLVMValueRef op2 = ir_llvm_value(g, instruction->op2); LLVMValueRef op3 = ir_llvm_value(g, instruction->op3); -- cgit v1.2.3 From 3c827be876a39cbe199e5cd7c6e90edef3a090b5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 27 Jan 2022 21:36:43 -0700 Subject: fix invalid const bitcast of f80 LLVM bitcast wants integers that match the number of bits. So the const bitcast has to use an i80, not an i128. This commit makes the behavior tests fail for me, so it seems I did not correctly construct the type. But it gets rid of the LLVM segfault. I noticed that the strategy of memcpy the buf worked if I simply did an LLVMConstTrunc() on the i128 to make it into an i80 before the LLVMConstBitCast(). But is that correct in the face of different endianness? I'm not sure. --- src/stage1/codegen.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'src/stage1/codegen.cpp') diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 2051d39a7c..58670cf822 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -8094,10 +8094,18 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n case 64: return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f64); case 80: { - uint64_t buf[2]; - memcpy(&buf, &const_val->data.x_f80, 16); - LLVMValueRef as_int = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, buf); - return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry)); + LLVMTypeRef llvm_i80 = LLVMIntType(80); + LLVMValueRef x; + if (g->is_big_endian) { + x = LLVMConstInt(llvm_i80, const_val->data.x_f80.signExp, false); + x = LLVMConstShl(x, LLVMConstInt(llvm_i80, 64, false)); + x = LLVMConstOr(x, LLVMConstInt(llvm_i80, const_val->data.x_f80.signif, false)); + } else { + x = LLVMConstInt(llvm_i80, const_val->data.x_f80.signif, false); + x = LLVMConstShl(x, LLVMConstInt(llvm_i80, 16, false)); + x = LLVMConstOr(x, LLVMConstInt(llvm_i80, const_val->data.x_f80.signExp, false)); + } + return LLVMConstBitCast(x, get_llvm_type(g, type_entry)); } case 128: { -- cgit v1.2.3 From a0a71709bc2104c708f045fbb42c6247aff136ac Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 28 Jan 2022 11:40:19 -0700 Subject: stage1: lower const f80 a different way this way passes the behavior tests --- src/stage1/codegen.cpp | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) (limited to 'src/stage1/codegen.cpp') diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 58670cf822..b97f009d62 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -8094,33 +8094,32 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n case 64: return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f64); case 80: { - LLVMTypeRef llvm_i80 = LLVMIntType(80); - LLVMValueRef x; - if (g->is_big_endian) { - x = LLVMConstInt(llvm_i80, const_val->data.x_f80.signExp, false); - x = LLVMConstShl(x, LLVMConstInt(llvm_i80, 64, false)); - x = LLVMConstOr(x, LLVMConstInt(llvm_i80, const_val->data.x_f80.signif, false)); - } else { - x = LLVMConstInt(llvm_i80, const_val->data.x_f80.signif, false); - x = LLVMConstShl(x, LLVMConstInt(llvm_i80, 16, false)); - x = LLVMConstOr(x, LLVMConstInt(llvm_i80, const_val->data.x_f80.signExp, false)); - } - return LLVMConstBitCast(x, get_llvm_type(g, type_entry)); + uint64_t buf[2]; + memcpy(&buf, &const_val->data.x_f80, 16); +#if ZIG_BYTE_ORDER == ZIG_BIG_ENDIAN + uint64_t tmp = buf[0]; + buf[0] = buf[1]; + buf[1] = tmp; +#endif + LLVMValueRef as_i128 = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, buf); + LLVMValueRef as_int = LLVMConstTrunc(as_i128, LLVMIntType(80)); + return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry)); } case 128: { uint64_t buf[2]; - // LLVM seems to require that the lower half of the f128 be placed first in the buffer. - #if defined(ZIG_BYTE_ORDER) && ZIG_BYTE_ORDER == ZIG_LITTLE_ENDIAN - buf[0] = const_val->data.x_f128.v[0]; - buf[1] = const_val->data.x_f128.v[1]; - #elif defined(ZIG_BYTE_ORDER) && ZIG_BYTE_ORDER == ZIG_BIG_ENDIAN - buf[0] = const_val->data.x_f128.v[1]; - buf[1] = const_val->data.x_f128.v[0]; - #else - #error Unsupported endian - #endif + // LLVM seems to require that the lower half of the f128 be + // placed first in the buffer. +#if ZIG_BYTE_ORDER == ZIG_LITTLE_ENDIAN + buf[0] = const_val->data.x_f128.v[0]; + buf[1] = const_val->data.x_f128.v[1]; +#elif ZIG_BYTE_ORDER == ZIG_BIG_ENDIAN + buf[0] = const_val->data.x_f128.v[1]; + buf[1] = const_val->data.x_f128.v[0]; +#else +#error Unsupported endian +#endif LLVMValueRef as_int = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, buf); return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry)); -- cgit v1.2.3