From 2feb231105cb50b9a435f61beed69f2284b59865 Mon Sep 17 00:00:00 2001 From: ManDude <7569514+ManDude@users.noreply.github.com> Date: Sat, 29 Apr 2023 21:10:51 +0100 Subject: [PATCH] less branches for division + fix divide by zeros (#2585) Slight change to float divide operations (again). Now it only turns into inverse multiplication if the float is a power of 2 (positive or negative). Non-zero immediate divisors will be compiled as regular float divisions but will forgo the extra branches and checks for divide by zero. Also fixes #2584 --- common/goos/Object.cpp | 14 ++++++++++++++ common/goos/Object.h | 2 ++ goalc/compiler/Compiler.h | 3 ++- goalc/compiler/compilation/Math.cpp | 22 +++++++++++++--------- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/common/goos/Object.cpp b/common/goos/Object.cpp index 328e331b6..465304b15 100644 --- a/common/goos/Object.cpp +++ b/common/goos/Object.cpp @@ -41,6 +41,7 @@ #include "Object.h" #include +#include #include "common/util/FileUtil.h" #include "common/util/print_float.h" @@ -207,6 +208,19 @@ Object build_list(std::vector&& objects) { return result; } +/*! + * Is this a float object that's a power of two? + * NOTE: assumes 64-bit float. + */ +bool Object::is_power_of_2_float() const { + FloatType val = as_float(); + u64 val_i = -1; + memcpy(&val_i, &val, sizeof(val)); + u64 mantissa = val_i & ((1LL << 52) - 1); + u64 exponent = (val_i >> 52) & ((1LL << 11) - 1); + return mantissa == 0 && exponent != 0 && exponent != ((1LL << 11) - 1); +} + /*! * Compare two objects for equality. * Does "expensive" checking. diff --git a/common/goos/Object.h b/common/goos/Object.h index f6420e74a..846650268 100644 --- a/common/goos/Object.h +++ b/common/goos/Object.h @@ -309,6 +309,8 @@ class Object { bool is_macro() const { return type == ObjectType::MACRO; } bool is_string_hash_table() const { return type == ObjectType::STRING_HASH_TABLE; } + bool is_power_of_2_float() const; + bool operator==(const Object& other) const; bool operator!=(const Object& other) const { return !((*this) == other); } }; diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index 104e0231f..36313462c 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -339,7 +339,8 @@ class Compiler { const TypeSpec& result_type, RegVal* a, RegVal* b, - Env* env); + Env* env, + bool imm_divisor); Val* compile_format_string(const goos::Object& form, Env* env, diff --git a/goalc/compiler/compilation/Math.cpp b/goalc/compiler/compilation/Math.cpp index 2c89ba04a..5a8b80336 100644 --- a/goalc/compiler/compilation/Math.cpp +++ b/goalc/compiler/compilation/Math.cpp @@ -373,11 +373,12 @@ Val* Compiler::compile_floating_point_division(const goos::Object& form, const TypeSpec& result_type, RegVal* a, RegVal* b, - Env* env) { + Env* env, + bool imm_divisor) { constexpr bool use_accurate = true; auto result = env->make_fpr(result_type); - if (use_accurate) { + if (use_accurate && !imm_divisor) { auto fenv = env->function_env(); auto end_label = fenv->alloc_unnamed_label(); end_label->func = fenv; @@ -404,8 +405,9 @@ Val* Compiler::compile_floating_point_division(const goos::Object& form, branch_ir_ref->mark_as_resolved(); branch_ir_ref->label.idx = fenv->code().size(); + // code for dividing by zero auto flt_max = compile_integer(0x7f7fffff, env)->to_gpr(form, env); - auto mask = compile_integer(0xf0000000, env)->to_gpr(form, env); + auto mask = compile_integer(0x80000000, env)->to_gpr(form, env); auto temp_int = env->make_gpr(result_type); env->emit_ir(form, temp_int, a); env->emit_ir(form, IntegerMathKind::AND_64, temp_int, mask); @@ -482,17 +484,19 @@ Val* Compiler::compile_div(const goos::Object& form, const goos::Object& rest, E case MATH_FLOAT: { const auto& divisor = args.unnamed.at(1); // in original GOAL, immediate divisions were turned into inverse multiplications - if (divisor.is_float() && !divisor.is_float(0)) { + if (divisor.is_float() && !divisor.is_float(0) && divisor.is_power_of_2_float()) { // TODO eventually this should be smarter somehow - auto as_inverse_mult = - pretty_print::build_list({pretty_print::to_symbol("*"), args.unnamed.at(0), - goos::Object::make_float(1.0 / divisor.as_float())}); - return compile_mul(as_inverse_mult, as_inverse_mult.as_pair()->cdr, env); + return compile_mul( + form, + pretty_print::build_list( + {goos::Object::make_float(1.0 / divisor.as_float()), args.unnamed.at(0)}), + env); } else { auto a = first_val->to_fpr(form, env); auto b = to_math_type(form, compile_error_guard(divisor, env), math_type, env) ->to_fpr(form, env); - return compile_floating_point_division(form, first_type, a, b, env); + return compile_floating_point_division(form, first_type, a, b, env, + divisor.is_float() && !divisor.is_float(0)); } }