make f80 less hacky; lower as u80 on non-x86

Get rid of `std.math.F80Repr`. Instead of trying to match the memory
layout of f80, we treat it as a value, same as the other floating point
types. The functions `make_f80` and `break_f80` are introduced to
compose an f80 value out of its parts, and the inverse operation.

stage2 LLVM backend: fix pointer to zero length array tripping LLVM
assertion. It now checks for when the element type is a zero-bit type
and lowers such thing the same way that pointers to other zero-bit types
are lowered.

Both stage1 and stage2 LLVM backends are adjusted so that f80 is lowered
as x86_fp80 on x86_64 and i386 architectures, and identical to a u80 on
others. LLVM constants are lowered in a less hacky way now that #10860
is fixed, by using the expression `(exp << 64) | fraction` using llvm
constants.

Sema is improved to handle c_longdouble by recursively handling it
correctly for whatever the float bit width is. In both stage1 and
stage2.
This commit is contained in:
Andrew Kelley
2022-02-10 22:06:43 -07:00
committed by Jakub Konka
parent 1c23321d03
commit a024aff932
10 changed files with 200 additions and 110 deletions

View File

@@ -8195,17 +8195,15 @@ 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);
#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);
if (!target_has_f80(g->zig_target)) return as_i128;
LLVMValueRef as_int = LLVMConstTrunc(as_i128, LLVMIntType(80));
return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry));
LLVMTypeRef llvm_i80 = LLVMIntType(80);
LLVMValueRef 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));
if (target_has_f80(g->zig_target)) {
return LLVMConstBitCast(x, LLVMX86FP80Type());
} else {
return x;
}
}
case 128:
{
@@ -9429,32 +9427,36 @@ static void define_builtin_types(CodeGen *g) {
{
ZigType *entry = new_type_table_entry(ZigTypeIdFloat);
unsigned u64_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMInt64Type());
entry->size_in_bits = 80;
if (u64_alignment >= 8) {
entry->size_in_bits = 128;
entry->abi_size = 16;
entry->abi_align = 16;
} else if (u64_alignment >= 4) {
entry->size_in_bits = 96;
entry->abi_size = 12;
entry->abi_align = 4;
} else {
entry->size_in_bits = 80;
entry->abi_size = 10;
entry->abi_align = 2;
}
if (target_has_f80(g->zig_target)) {
entry->llvm_type = LLVMX86FP80Type();
} else {
// We use an int here instead of x86_fp80 because on targets such as arm,
// LLVM will give "ERROR: Cannot select" for any instructions involving
// the x86_fp80 type.
entry->llvm_type = get_int_type(g, false, entry->size_in_bits)->llvm_type;
}
buf_init_from_str(&entry->name, "f80");
entry->data.floating.bit_count = 80;
switch (g->zig_target->arch) {
case ZigLLVM_x86_64:
entry->llvm_type = LLVMX86FP80Type();
entry->abi_size = 16;
entry->abi_align = 16;
break;
case ZigLLVM_x86:
entry->llvm_type = LLVMX86FP80Type();
entry->abi_size = 12;
entry->abi_align = 4;
break;
default: {
// We use an int here instead of x86_fp80 because on targets such as arm,
// LLVM will give "ERROR: Cannot select" for any instructions involving
// the x86_fp80 type.
ZigType *u80_ty = get_int_type(g, false, 80);
assert(!target_has_f80(g->zig_target));
assert(u80_ty->size_in_bits == entry->size_in_bits);
entry->llvm_type = get_llvm_type(g, u80_ty);
entry->abi_size = u80_ty->abi_size;
entry->abi_align = u80_ty->abi_align;
break;
}
}
entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, ZigLLVMEncoding_DW_ATE_unsigned());