diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 5df72f0c5..d05fc98f0 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1802,6 +1802,12 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { case f32: fmt_float(fi, f64(a), 32, verb); case f64: fmt_float(fi, a, 64, verb); + case f32le: fmt_float(fi, f64(a), 32, verb); + case f64le: fmt_float(fi, f64(a), 64, verb); + + case f32be: fmt_float(fi, f64(a), 32, verb); + case f64be: fmt_float(fi, f64(a), 64, verb); + case complex64: fmt_complex(fi, complex128(a), 64, verb); case complex128: fmt_complex(fi, a, 128, verb); diff --git a/core/reflect/types.odin b/core/reflect/types.odin index d21d6eeb5..cec2529ac 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -363,6 +363,11 @@ write_type :: proc(buf: ^strings.Builder, ti: ^Type_Info) { case Type_Info_Float: write_byte(buf, 'f'); write_i64(buf, i64(8*ti.size), 10); + switch info.endianness { + case .Platform: // Okay + case .Little: write_string(buf, "le"); + case .Big: write_string(buf, "be"); + } case Type_Info_Complex: write_string(buf, "complex"); write_i64(buf, i64(8*ti.size), 10); diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 3ef992e8e..d906c28b7 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -57,7 +57,7 @@ Type_Info_Struct_Soa_Kind :: enum u8 { Type_Info_Named :: struct {name: string, base: ^Type_Info}; Type_Info_Integer :: struct {signed: bool, endianness: Platform_Endianness}; Type_Info_Rune :: struct {}; -Type_Info_Float :: struct {}; +Type_Info_Float :: struct {endianness: Platform_Endianness}; Type_Info_Complex :: struct {}; Type_Info_Quaternion :: struct {}; Type_Info_String :: struct {is_cstring: bool}; diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index 4b3039802..9dd166d8e 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -18,6 +18,22 @@ bswap_128 :: proc "none" (x: u128) -> u128 { return u128(bswap_64(u64(x))) | u128(bswap_64(u64(x>>64))); } + +bswap_f32 :: proc "none" (f: f32) -> f32 { + x := transmute(u32)f; + z := x>>24 | (x>>8)&0xff00 | (x<<8)&0xff0000 | x<<24; + return transmute(f32)z; + +} + +bswap_f64 :: proc "none" (f: f64) -> f64 { + x := transmute(u64)f; + z := u64(bswap_32(u32(x))) | u64(bswap_32(u32(x>>32))); + return transmute(f64)z; +} + + + ptr_offset :: inline proc "contextless" (ptr: $P/^$T, n: int) -> P { new := int(uintptr(ptr)) + size_of(T)*n; return P(uintptr(new)); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index a9afbf8a4..12712443f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1455,6 +1455,12 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ case Basic_f64: return true; + case Basic_f32le: + case Basic_f64le: + case Basic_f32be: + case Basic_f64be: + return true; + case Basic_UntypedFloat: return true; diff --git a/src/checker.cpp b/src/checker.cpp index 678339362..323adac2a 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1708,6 +1708,9 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { str_lit("bswap_32"), str_lit("bswap_64"), str_lit("bswap_128"), + + str_lit("bswap_f32"), + str_lit("bswap_f64"), }; for (isize i = 0; i < gb_count_of(required_runtime_entities); i++) { add_dependency_to_set(c, scope_lookup(c->info.runtime_package->scope, required_runtime_entities[i])); diff --git a/src/ir.cpp b/src/ir.cpp index ecf911461..31d4036b1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3297,7 +3297,6 @@ irValue *ir_emit_package_call(irProcedure *proc, char const *package_name_, char } - void ir_emit_defer_stmts(irProcedure *proc, irDeferExitKind kind, irBlock *block) { isize count = proc->defer_stmts.count; isize i = count; @@ -4320,8 +4319,18 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue * goto handle_op; } Type *platform_type = integer_endian_type_to_platform_type(type); - irValue *x = ir_emit_byte_swap(proc, left, integer_endian_type_to_platform_type(t_left)); - irValue *y = ir_emit_byte_swap(proc, right, integer_endian_type_to_platform_type(t_right)); + irValue *x = ir_emit_conv(proc, left, integer_endian_type_to_platform_type(t_left)); + irValue *y = ir_emit_conv(proc, right, integer_endian_type_to_platform_type(t_right)); + + irValue *res = ir_emit_arith(proc, op, x, y, platform_type); + + return ir_emit_byte_swap(proc, res, type); + } + + if (is_type_float(type) && is_type_different_to_arch_endianness(type)) { + Type *platform_type = integer_endian_type_to_platform_type(type); + irValue *x = ir_emit_conv(proc, left, integer_endian_type_to_platform_type(t_left)); + irValue *y = ir_emit_conv(proc, right, integer_endian_type_to_platform_type(t_right)); irValue *res = ir_emit_arith(proc, op, x, y, platform_type); @@ -4748,6 +4757,12 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal irValue *y = ir_emit_byte_swap(proc, right, platform_type); return ir_emit(proc, ir_instr_binary_op(proc, op_kind, x, y, t_llvm_bool)); } + if (is_type_float(t) && is_type_different_to_arch_endianness(t)) { + Type *platform_type = integer_endian_type_to_platform_type(t); + irValue *x = ir_emit_byte_swap(proc, left, platform_type); + irValue *y = ir_emit_byte_swap(proc, right, platform_type); + return ir_emit(proc, ir_instr_binary_op(proc, op_kind, x, y, t_llvm_bool)); + } } return ir_emit(proc, ir_instr_binary_op(proc, op_kind, left, right, t_llvm_bool)); @@ -5269,6 +5284,29 @@ irValue *ir_emit_byte_swap(irProcedure *proc, irValue *value, Type *t) { return value; } GB_ASSERT(type_size_of(vt) == type_size_of(t)); + if (is_type_float(t)) { + i64 sz = type_size_of(t); + + auto args = array_make(ir_allocator(), 1); + args[0] = value; + + char const *proc_name = nullptr; + switch (sz*8) { + case 32: proc_name = "bswap_f32"; break; + case 64: proc_name = "bswap_f64"; break; + } + GB_ASSERT(proc_name != nullptr); + + String name = make_string_c(proc_name); + + AstPackage *p = proc->module->info->runtime_package; + Entity *e = scope_lookup_current(p->scope, name); + irValue **found = map_get(&proc->module->values, hash_entity(e)); + GB_ASSERT_MSG(found != nullptr, "%.*s", LIT(name)); + irValue *gp = *found; + + return ir_emit(proc, ir_instr_call(proc, gp, nullptr, args, t, nullptr, ProcInlining_none)); + } return ir_emit(proc, ir_instr_conv(proc, irConv_byteswap, value, vt, t)); } @@ -5423,13 +5461,34 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { // float -> float if (is_type_float(src) && is_type_float(dst)) { + GB_ASSERT(!are_types_identical(src, dst)); gbAllocator a = ir_allocator(); i64 sz = type_size_of(src); i64 dz = type_size_of(dst); irConvKind kind = irConv_fptrunc; + if (dz == sz) { + if (types_have_same_internal_endian(src, dst)) { + return ir_emit_transmute(proc, value, t); + } else { + return ir_emit_byte_swap(proc, value, t); + } + } if (dz >= sz) { kind = irConv_fpext; } + if (is_type_different_to_arch_endianness(src) || is_type_different_to_arch_endianness(dst)) { + Type *platform_src_type = integer_endian_type_to_platform_type(src); + Type *platform_dst_type = integer_endian_type_to_platform_type(dst); + irValue *res = nullptr; + res = ir_emit_conv(proc, value, platform_src_type); + res = ir_emit_conv(proc, res, platform_dst_type); + if (is_type_different_to_arch_endianness(dst)) { + res = ir_emit_byte_swap(proc, res, t); + } + return ir_emit_conv(proc, res, t); + } + + return ir_emit(proc, ir_instr_conv(proc, kind, value, src_type, t)); } @@ -5738,6 +5797,9 @@ irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t) { irValue *ptr = ir_emit_uintptr_to_ptr(proc, value, t_rawptr); return ir_emit_bitcast(proc, ptr, dst); } + if (is_type_float(src) && is_type_float(dst)) { + return ir_emit_bitcast(proc, value, t); + } if (is_type_integer(src) && (is_type_pointer(dst) || is_type_cstring(dst))) { Type *vt = core_type(ir_type(value)); @@ -11441,9 +11503,26 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info // case Basic_f16: case Basic_f32: case Basic_f64: - tag = ir_emit_conv(proc, variant_ptr, t_type_info_float_ptr); + case Basic_f32le: + case Basic_f64le: + case Basic_f32be: + case Basic_f64be: + { + tag = ir_emit_conv(proc, variant_ptr, t_type_info_float_ptr); + + // NOTE(bill): This is matches the runtime layout + u8 endianness_value = 0; + if (t->Basic.flags & BasicFlag_EndianLittle) { + endianness_value = 1; + } else if (t->Basic.flags & BasicFlag_EndianBig) { + endianness_value = 2; + } + irValue *endianness = ir_const_u8(endianness_value); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), endianness); + } break; + // case Basic_complex32: case Basic_complex64: case Basic_complex128: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 32cc3809d..ebb729420 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -449,6 +449,13 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) { case Basic_f32: ir_write_str_lit(f, "float"); return; case Basic_f64: ir_write_str_lit(f, "double"); return; + + case Basic_f32le: ir_write_str_lit(f, "float"); return; + case Basic_f64le: ir_write_str_lit(f, "double"); return; + + case Basic_f32be: ir_write_str_lit(f, "float"); return; + case Basic_f64be: ir_write_str_lit(f, "double"); return; + // case Basic_complex32: ir_write_str_lit(f, "%%..complex32"); return; case Basic_complex64: ir_write_str_lit(f, "%..complex64"); return; case Basic_complex128: ir_write_str_lit(f, "%..complex128"); return; @@ -834,6 +841,8 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * type = core_type(type); u64 u_64 = bit_cast(value.value_float); u32 u_32 = bit_cast(cast(f32)value.value_float); + + #if 0 switch (type->Basic.kind) { case Basic_f32: @@ -861,13 +870,38 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * } #else switch (type->Basic.kind) { - case Basic_f32: { + case Basic_f32: ir_fprintf(f, "bitcast (i32 %u to float)", u_32); break; - } + case Basic_f32le: + if (build_context.endian_kind != TargetEndian_Little) { + u_32 = gb_endian_swap32(u_32); + } + ir_fprintf(f, "bitcast (i32 %u to float)", u_32); + break; + case Basic_f32be: + if (build_context.endian_kind != TargetEndian_Big) { + u_32 = gb_endian_swap32(u_32); + } + ir_fprintf(f, "bitcast (i32 %u to float)", u_32); + break; + case Basic_f64: ir_fprintf(f, "0x%016llx", u_64); break; + case Basic_f64le: + if (build_context.endian_kind != TargetEndian_Little) { + u_64 = gb_endian_swap64(u_64); + } + ir_fprintf(f, "0x%016llx", u_64); + break; + case Basic_f64be: + if (build_context.endian_kind != TargetEndian_Big) { + u_64 = gb_endian_swap64(u_64); + } + ir_fprintf(f, "0x%016llx", u_64); + break; + default: ir_fprintf(f, "0x%016llx", u_64); break; diff --git a/src/types.cpp b/src/types.cpp index 2ab0897da..52db2ee44 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -64,6 +64,12 @@ enum BasicKind { Basic_i128be, Basic_u128be, + Basic_f32le, + Basic_f64le, + + Basic_f32be, + Basic_f64be, + // Untyped types Basic_UntypedBool, Basic_UntypedInteger, @@ -449,6 +455,12 @@ gb_global Type basic_types[] = { {Type_Basic, {Basic_i128be, BasicFlag_Integer | BasicFlag_EndianBig, 16, STR_LIT("i128be")}}, {Type_Basic, {Basic_u128be, BasicFlag_Integer | BasicFlag_Unsigned | BasicFlag_EndianBig, 16, STR_LIT("u128be")}}, + {Type_Basic, {Basic_f32le, BasicFlag_Float | BasicFlag_EndianLittle, 4, STR_LIT("f32le")}}, + {Type_Basic, {Basic_f64le, BasicFlag_Float | BasicFlag_EndianLittle, 8, STR_LIT("f64le")}}, + + {Type_Basic, {Basic_f32be, BasicFlag_Float | BasicFlag_EndianBig, 4, STR_LIT("f32be")}}, + {Type_Basic, {Basic_f64be, BasicFlag_Float | BasicFlag_EndianBig, 8, STR_LIT("f64be")}}, + // Untyped types {Type_Basic, {Basic_UntypedBool, BasicFlag_Boolean | BasicFlag_Untyped, 0, STR_LIT("untyped bool")}}, {Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped integer")}}, @@ -1316,6 +1328,11 @@ bool is_type_endian_little(Type *t) { return is_type_integer_endian_little(t); } +bool types_have_same_internal_endian(Type *a, Type *b) { + return is_type_endian_little(a) == is_type_endian_little(b); +} + + bool is_type_dereferenceable(Type *t) { if (is_type_rawptr(t)) { return false; @@ -1357,6 +1374,11 @@ Type *integer_endian_type_to_platform_type(Type *t) { case Basic_u32be: return t_u32; case Basic_i64be: return t_i64; case Basic_u64be: return t_u64; + + case Basic_f32le: return t_f32; + case Basic_f32be: return t_f32; + case Basic_f64le: return t_f64; + case Basic_f64be: return t_f64; } return t;