diff --git a/Userland/Libraries/LibJIT/X86_64/Assembler.h b/Userland/Libraries/LibJIT/X86_64/Assembler.h index ba57bd136c..79b96cf81f 100644 --- a/Userland/Libraries/LibJIT/X86_64/Assembler.h +++ b/Userland/Libraries/LibJIT/X86_64/Assembler.h @@ -582,6 +582,15 @@ struct X86_64Assembler { emit_modrm_slash(0, dst); } + void mov_if(Condition condition, Operand dst, Operand src) + { + VERIFY(dst.type == Operand::Type::Reg && src.type == Operand::Type::Reg); + emit_rex_for_rm(dst, src, REX_W::Yes); + emit8(0x0f); + emit8(0x40 | to_underlying(condition)); + emit_modrm_rm(dst, src); + } + void sign_extend_32_to_64_bits(Reg reg) { mov32(Operand::Register(reg), Operand::Register(reg), Extension::SignExtend); @@ -989,6 +998,14 @@ struct X86_64Assembler { } } + void neg32(Operand reg) + { + VERIFY(reg.type == Operand::Type::Reg); + emit_rex_for_slash(reg, REX_W::No); + emit8(0xf7); + emit_modrm_slash(3, reg); + } + void convert_i32_to_double(Operand dst, Operand src) { VERIFY(dst.type == Operand::Type::FReg); diff --git a/Userland/Libraries/LibJS/Bytecode/Builtins.h b/Userland/Libraries/LibJS/Bytecode/Builtins.h index 200164c3af..b67de4b855 100644 --- a/Userland/Libraries/LibJS/Bytecode/Builtins.h +++ b/Userland/Libraries/LibJS/Bytecode/Builtins.h @@ -12,7 +12,8 @@ namespace JS::Bytecode { // TitleCaseName, snake_case_name, base, property, argument_count -#define JS_ENUMERATE_BUILTINS(O) +#define JS_ENUMERATE_BUILTINS(O) \ + O(MathAbs, math_abs, Math, abs, 1) enum class Builtin { #define DEFINE_BUILTIN_ENUM(name, ...) name, diff --git a/Userland/Libraries/LibJS/JIT/Compiler.cpp b/Userland/Libraries/LibJS/JIT/Compiler.cpp index 891b2a11fd..251f89253d 100644 --- a/Userland/Libraries/LibJS/JIT/Compiler.cpp +++ b/Userland/Libraries/LibJS/JIT/Compiler.cpp @@ -2587,7 +2587,7 @@ void Compiler::compile_call(Bytecode::Op::Call const& op) end.link(m_assembler); } -void Compiler::compile_builtin(Bytecode::Builtin builtin, [[maybe_unused]] Assembler::Label& slow_case, [[maybe_unused]] Assembler::Label& end) +void Compiler::compile_builtin(Bytecode::Builtin builtin, Assembler::Label& slow_case, Assembler::Label& end) { switch (builtin) { # define DEFINE_BUILTIN_CASE(name, snake_case_name, ...) \ @@ -2601,6 +2601,43 @@ void Compiler::compile_builtin(Bytecode::Builtin builtin, [[maybe_unused]] Assem } } +void Compiler::compile_builtin_math_abs(Assembler::Label& slow_case, Assembler::Label& end) +{ + branch_if_int32(ARG2, [&] { + // ARG2 &= 0xffffffff + m_assembler.mov32(Assembler::Operand::Register(ARG2), Assembler::Operand::Register(ARG2), Assembler::Extension::SignExtend); + + // if (ARG2 == INT32_MIN) goto slow_case; + m_assembler.jump_if( + Assembler::Operand::Register(ARG2), + Assembler::Condition::EqualTo, + Assembler::Operand::Imm(NumericLimits::min()), + slow_case); + + // accumulator = ARG2 < 0 ? -ARG2 : ARG2; + m_assembler.mov(Assembler::Operand::Register(CACHED_ACCUMULATOR), Assembler::Operand::Register(ARG2)); + m_assembler.neg32(Assembler::Operand::Register(CACHED_ACCUMULATOR)); + m_assembler.mov_if(Assembler::Condition::SignedLessThan, Assembler::Operand::Register(CACHED_ACCUMULATOR), Assembler::Operand::Register(ARG2)); + + // accumulator |= SHIFTED_INT32_TAG; + m_assembler.mov(Assembler::Operand::Register(GPR0), Assembler::Operand::Imm(SHIFTED_INT32_TAG)); + m_assembler.bitwise_or(Assembler::Operand::Register(CACHED_ACCUMULATOR), Assembler::Operand::Register(GPR0)); + + m_assembler.jump(end); + }); + + // if (ARG2.is_double()) goto slow_case; + m_assembler.mov(Assembler::Operand::Register(GPR0), Assembler::Operand::Imm(CANON_NAN_BITS)); + jump_if_not_double(ARG2, GPR0, GPR1, slow_case); + + // accumulator = ARG2 & 0x7fffffffffffffff + m_assembler.mov(Assembler::Operand::Register(GPR0), Assembler::Operand::Imm(0x7fffffffffffffff)); + m_assembler.bitwise_and(Assembler::Operand::Register(ARG2), Assembler::Operand::Register(GPR0)); + store_accumulator(ARG2); + + m_assembler.jump(end); +} + static Value cxx_call_with_argument_array(VM& vm, Value arguments, Value callee, Value this_value, Bytecode::Op::CallType call_type, Optional const& expression_string) { TRY_OR_SET_EXCEPTION(throw_if_needed_for_call(vm.bytecode_interpreter(), callee, call_type, expression_string)); diff --git a/Userland/Libraries/LibJS/Runtime/MathObject.cpp b/Userland/Libraries/LibJS/Runtime/MathObject.cpp index 67df8a45c9..5337f5c93a 100644 --- a/Userland/Libraries/LibJS/Runtime/MathObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/MathObject.cpp @@ -27,7 +27,7 @@ void MathObject::initialize(Realm& realm) auto& vm = this->vm(); Base::initialize(realm); u8 attr = Attribute::Writable | Attribute::Configurable; - define_native_function(realm, vm.names.abs, abs, 1, attr); + define_native_function(realm, vm.names.abs, abs, 1, attr, Bytecode::Builtin::MathAbs); define_native_function(realm, vm.names.random, random, 0, attr); define_native_function(realm, vm.names.sqrt, sqrt, 1, attr); define_native_function(realm, vm.names.floor, floor, 1, attr);