mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 11:26:18 -04:00
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
This commit is contained in:
parent
0d3e272876
commit
2feb231105
|
@ -41,6 +41,7 @@
|
||||||
#include "Object.h"
|
#include "Object.h"
|
||||||
|
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "common/util/FileUtil.h"
|
#include "common/util/FileUtil.h"
|
||||||
#include "common/util/print_float.h"
|
#include "common/util/print_float.h"
|
||||||
|
@ -207,6 +208,19 @@ Object build_list(std::vector<Object>&& objects) {
|
||||||
return result;
|
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.
|
* Compare two objects for equality.
|
||||||
* Does "expensive" checking.
|
* Does "expensive" checking.
|
||||||
|
|
|
@ -309,6 +309,8 @@ class Object {
|
||||||
bool is_macro() const { return type == ObjectType::MACRO; }
|
bool is_macro() const { return type == ObjectType::MACRO; }
|
||||||
bool is_string_hash_table() const { return type == ObjectType::STRING_HASH_TABLE; }
|
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;
|
||||||
bool operator!=(const Object& other) const { return !((*this) == other); }
|
bool operator!=(const Object& other) const { return !((*this) == other); }
|
||||||
};
|
};
|
||||||
|
|
|
@ -339,7 +339,8 @@ class Compiler {
|
||||||
const TypeSpec& result_type,
|
const TypeSpec& result_type,
|
||||||
RegVal* a,
|
RegVal* a,
|
||||||
RegVal* b,
|
RegVal* b,
|
||||||
Env* env);
|
Env* env,
|
||||||
|
bool imm_divisor);
|
||||||
|
|
||||||
Val* compile_format_string(const goos::Object& form,
|
Val* compile_format_string(const goos::Object& form,
|
||||||
Env* env,
|
Env* env,
|
||||||
|
|
|
@ -373,11 +373,12 @@ Val* Compiler::compile_floating_point_division(const goos::Object& form,
|
||||||
const TypeSpec& result_type,
|
const TypeSpec& result_type,
|
||||||
RegVal* a,
|
RegVal* a,
|
||||||
RegVal* b,
|
RegVal* b,
|
||||||
Env* env) {
|
Env* env,
|
||||||
|
bool imm_divisor) {
|
||||||
constexpr bool use_accurate = true;
|
constexpr bool use_accurate = true;
|
||||||
auto result = env->make_fpr(result_type);
|
auto result = env->make_fpr(result_type);
|
||||||
|
|
||||||
if (use_accurate) {
|
if (use_accurate && !imm_divisor) {
|
||||||
auto fenv = env->function_env();
|
auto fenv = env->function_env();
|
||||||
auto end_label = fenv->alloc_unnamed_label();
|
auto end_label = fenv->alloc_unnamed_label();
|
||||||
end_label->func = fenv;
|
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->mark_as_resolved();
|
||||||
branch_ir_ref->label.idx = fenv->code().size();
|
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 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);
|
auto temp_int = env->make_gpr(result_type);
|
||||||
env->emit_ir<IR_RegSet>(form, temp_int, a);
|
env->emit_ir<IR_RegSet>(form, temp_int, a);
|
||||||
env->emit_ir<IR_IntegerMath>(form, IntegerMathKind::AND_64, temp_int, mask);
|
env->emit_ir<IR_IntegerMath>(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: {
|
case MATH_FLOAT: {
|
||||||
const auto& divisor = args.unnamed.at(1);
|
const auto& divisor = args.unnamed.at(1);
|
||||||
// in original GOAL, immediate divisions were turned into inverse multiplications
|
// 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
|
// TODO eventually this should be smarter somehow
|
||||||
auto as_inverse_mult =
|
return compile_mul(
|
||||||
pretty_print::build_list({pretty_print::to_symbol("*"), args.unnamed.at(0),
|
form,
|
||||||
goos::Object::make_float(1.0 / divisor.as_float())});
|
pretty_print::build_list(
|
||||||
return compile_mul(as_inverse_mult, as_inverse_mult.as_pair()->cdr, env);
|
{goos::Object::make_float(1.0 / divisor.as_float()), args.unnamed.at(0)}),
|
||||||
|
env);
|
||||||
} else {
|
} else {
|
||||||
auto a = first_val->to_fpr(form, env);
|
auto a = first_val->to_fpr(form, env);
|
||||||
auto b = to_math_type(form, compile_error_guard(divisor, env), math_type, env)
|
auto b = to_math_type(form, compile_error_guard(divisor, env), math_type, env)
|
||||||
->to_fpr(form, 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue