diff options
author | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2017-05-30 12:48:17 +0200 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2017-05-30 12:48:17 +0200 |
commit | 881da28418d380042aa95a97f0cbd42560a64f7c (patch) | |
tree | a794dff3274695e99c651902dde93d934ea7a5af /Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h | |
parent | 7e104c57a70fdf551bb3d22a5d637cdcbc69dbea (diff) | |
parent | 0fcedcd17cc00d3dd44c718b3cb36c1033319671 (diff) | |
download | qtwebkit-881da28418d380042aa95a97f0cbd42560a64f7c.tar.gz |
Merge 'wip/next' into dev
Change-Id: Iff9ee5e23bb326c4371ec8ed81d56f2f05d680e9
Diffstat (limited to 'Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h')
-rw-r--r-- | Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h | 1475 |
1 files changed, 1339 insertions, 136 deletions
diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h b/Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h index f171dc408..f502a2551 100644 --- a/Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h +++ b/Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2014-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,21 +30,35 @@ #include "X86Assembler.h" #include "AbstractMacroAssembler.h" +#include <wtf/Optional.h> + +#if COMPILER(MSVC) +#include <intrin.h> +#endif namespace JSC { -class MacroAssemblerX86Common : public AbstractMacroAssembler<X86Assembler> { -protected: +class MacroAssemblerX86Common : public AbstractMacroAssembler<X86Assembler, MacroAssemblerX86Common> { +public: #if CPU(X86_64) - static const X86Registers::RegisterID scratchRegister = X86Registers::r11; + // Use this directly only if you're not generating code with it. + static const X86Registers::RegisterID s_scratchRegister = X86Registers::r11; + + // Use this when generating code so that we get enforcement of the disallowing of scratch register + // usage. + X86Registers::RegisterID scratchRegister() + { + RELEASE_ASSERT(m_allowScratchRegister); + return s_scratchRegister; + } #endif +protected: static const int DoubleConditionBitInvert = 0x10; static const int DoubleConditionBitSpecial = 0x20; static const int DoubleConditionBits = DoubleConditionBitInvert | DoubleConditionBitSpecial; public: - typedef X86Assembler::FPRegisterID FPRegisterID; typedef X86Assembler::XMMRegisterID XMMRegisterID; static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) @@ -73,6 +87,7 @@ public: NonZero = X86Assembler::ConditionNE }; + // FIXME: it would be neat to rename this to FloatingPointCondition in every assembler. enum DoubleCondition { // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. DoubleEqual = X86Assembler::ConditionE | DoubleConditionBitSpecial, @@ -94,16 +109,11 @@ public: DoubleConditionBits_should_not_interfere_with_X86Assembler_Condition_codes); static const RegisterID stackPointerRegister = X86Registers::esp; - -#if ENABLE(JIT_CONSTANT_BLINDING) + static const RegisterID framePointerRegister = X86Registers::ebp; + + static bool canBlind() { return true; } static bool shouldBlindForSpecificArch(uint32_t value) { return value >= 0x00ffffff; } -#if CPU(X86_64) static bool shouldBlindForSpecificArch(uint64_t value) { return value >= 0x00ffffff; } -#if OS(DARWIN) // On 64-bit systems other than DARWIN uint64_t and uintptr_t are the same type so overload is prohibited. - static bool shouldBlindForSpecificArch(uintptr_t value) { return value >= 0x00ffffff; } -#endif -#endif -#endif // Integer arithmetic operations: // @@ -122,9 +132,37 @@ public: m_assembler.addl_im(imm.m_value, address.offset, address.base); } + void add32(TrustedImm32 imm, BaseIndex address) + { + m_assembler.addl_im(imm.m_value, address.offset, address.base, address.index, address.scale); + } + + void add8(TrustedImm32 imm, Address address) + { + m_assembler.addb_im(imm.m_value, address.offset, address.base); + } + + void add8(TrustedImm32 imm, BaseIndex address) + { + m_assembler.addb_im(imm.m_value, address.offset, address.base, address.index, address.scale); + } + + void add16(TrustedImm32 imm, Address address) + { + m_assembler.addw_im(imm.m_value, address.offset, address.base); + } + + void add16(TrustedImm32 imm, BaseIndex address) + { + m_assembler.addw_im(imm.m_value, address.offset, address.base, address.index, address.scale); + } + void add32(TrustedImm32 imm, RegisterID dest) { - m_assembler.addl_ir(imm.m_value, dest); + if (imm.m_value == 1) + m_assembler.inc_r(dest); + else + m_assembler.addl_ir(imm.m_value, dest); } void add32(Address src, RegisterID dest) @@ -137,10 +175,65 @@ public: m_assembler.addl_rm(src, dest.offset, dest.base); } + void add32(RegisterID src, BaseIndex dest) + { + m_assembler.addl_rm(src, dest.offset, dest.base, dest.index, dest.scale); + } + + void add8(RegisterID src, Address dest) + { + m_assembler.addb_rm(src, dest.offset, dest.base); + } + + void add8(RegisterID src, BaseIndex dest) + { + m_assembler.addb_rm(src, dest.offset, dest.base, dest.index, dest.scale); + } + + void add16(RegisterID src, Address dest) + { + m_assembler.addw_rm(src, dest.offset, dest.base); + } + + void add16(RegisterID src, BaseIndex dest) + { + m_assembler.addw_rm(src, dest.offset, dest.base, dest.index, dest.scale); + } + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) { + if (!imm.m_value) { + zeroExtend32ToPtr(src, dest); + return; + } + + if (src == dest) { + add32(imm, dest); + return; + } + m_assembler.leal_mr(imm.m_value, src, dest); } + + void add32(RegisterID a, RegisterID b, RegisterID dest) + { + x86Lea32(BaseIndex(a, b, TimesOne), dest); + } + + void x86Lea32(BaseIndex index, RegisterID dest) + { + if (!index.scale && !index.offset) { + if (index.base == dest) { + add32(index.index, dest); + return; + } + if (index.index == dest) { + add32(index.base, dest); + return; + } + } + m_assembler.leal_mr(index.offset, index.base, index.index, index.scale, dest); + } void and32(RegisterID src, RegisterID dest) { @@ -174,24 +267,55 @@ public: else if (op1 == dest) and32(op2, dest); else { - move(op2, dest); + move32IfNeeded(op2, dest); and32(op1, dest); } } + void and32(Address op1, RegisterID op2, RegisterID dest) + { + move32IfNeeded(op2, dest); + and32(op1, dest); + } + + void and32(RegisterID op1, Address op2, RegisterID dest) + { + move32IfNeeded(op1, dest); + and32(op2, dest); + } + void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) { - move(src, dest); + move32IfNeeded(src, dest); and32(imm, dest); } - void lshift32(RegisterID shift_amount, RegisterID dest) + void countLeadingZeros32(RegisterID src, RegisterID dst) { - ASSERT(shift_amount != dest); + if (supportsLZCNT()) { + m_assembler.lzcnt_rr(src, dst); + return; + } + m_assembler.bsr_rr(src, dst); + clz32AfterBsr(dst); + } + void countLeadingZeros32(Address src, RegisterID dst) + { + if (supportsLZCNT()) { + m_assembler.lzcnt_mr(src.offset, src.base, dst); + return; + } + m_assembler.bsr_mr(src.offset, src.base, dst); + clz32AfterBsr(dst); + } + + void lshift32(RegisterID shift_amount, RegisterID dest) + { if (shift_amount == X86Registers::ecx) m_assembler.shll_CLr(dest); else { + ASSERT(shift_amount != dest); // On x86 we can only shift by ecx; if asked to shift by another register we'll // need rejig the shift amount into ecx first, and restore the registers afterwards. // If we dest is ecx, then shift the swapped register! @@ -205,8 +329,7 @@ public: { ASSERT(shift_amount != dest); - if (src != dest) - move(src, dest); + move32IfNeeded(src, dest); lshift32(shift_amount, dest); } @@ -217,8 +340,7 @@ public: void lshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) { - if (src != dest) - move(src, dest); + move32IfNeeded(src, dest); lshift32(imm, dest); } @@ -227,16 +349,62 @@ public: m_assembler.imull_rr(src, dest); } + void mul32(RegisterID src1, RegisterID src2, RegisterID dest) + { + if (src2 == dest) { + m_assembler.imull_rr(src1, dest); + return; + } + move32IfNeeded(src1, dest); + m_assembler.imull_rr(src2, dest); + } + void mul32(Address src, RegisterID dest) { m_assembler.imull_mr(src.offset, src.base, dest); } + + void mul32(Address src1, RegisterID src2, RegisterID dest) + { + move32IfNeeded(src2, dest); + mul32(src1, dest); + } + + void mul32(RegisterID src1, Address src2, RegisterID dest) + { + move32IfNeeded(src1, dest); + mul32(src2, dest); + } void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) { m_assembler.imull_i32r(src, imm.m_value, dest); } + void x86ConvertToDoubleWord32() + { + m_assembler.cdq(); + } + + void x86ConvertToDoubleWord32(RegisterID eax, RegisterID edx) + { + ASSERT_UNUSED(eax, eax == X86Registers::eax); + ASSERT_UNUSED(edx, edx == X86Registers::edx); + x86ConvertToDoubleWord32(); + } + + void x86Div32(RegisterID denominator) + { + m_assembler.idivl_r(denominator); + } + + void x86Div32(RegisterID eax, RegisterID edx, RegisterID denominator) + { + ASSERT_UNUSED(eax, eax == X86Registers::eax); + ASSERT_UNUSED(edx, edx == X86Registers::edx); + x86Div32(denominator); + } + void neg32(RegisterID srcDest) { m_assembler.negl_r(srcDest); @@ -279,24 +447,36 @@ public: else if (op1 == dest) or32(op2, dest); else { - move(op2, dest); + move32IfNeeded(op2, dest); or32(op1, dest); } } + void or32(Address op1, RegisterID op2, RegisterID dest) + { + move32IfNeeded(op2, dest); + or32(op1, dest); + } + + void or32(RegisterID op1, Address op2, RegisterID dest) + { + move32IfNeeded(op1, dest); + or32(op2, dest); + } + void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) { - move(src, dest); + move32IfNeeded(src, dest); or32(imm, dest); } void rshift32(RegisterID shift_amount, RegisterID dest) { - ASSERT(shift_amount != dest); - if (shift_amount == X86Registers::ecx) m_assembler.sarl_CLr(dest); else { + ASSERT(shift_amount != dest); + // On x86 we can only shift by ecx; if asked to shift by another register we'll // need rejig the shift amount into ecx first, and restore the registers afterwards. // If we dest is ecx, then shift the swapped register! @@ -310,8 +490,7 @@ public: { ASSERT(shift_amount != dest); - if (src != dest) - move(src, dest); + move32IfNeeded(src, dest); rshift32(shift_amount, dest); } @@ -322,18 +501,17 @@ public: void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) { - if (src != dest) - move(src, dest); + move32IfNeeded(src, dest); rshift32(imm, dest); } void urshift32(RegisterID shift_amount, RegisterID dest) { - ASSERT(shift_amount != dest); - if (shift_amount == X86Registers::ecx) m_assembler.shrl_CLr(dest); else { + ASSERT(shift_amount != dest); + // On x86 we can only shift by ecx; if asked to shift by another register we'll // need rejig the shift amount into ecx first, and restore the registers afterwards. // If we dest is ecx, then shift the swapped register! @@ -347,8 +525,7 @@ public: { ASSERT(shift_amount != dest); - if (src != dest) - move(src, dest); + move32IfNeeded(src, dest); urshift32(shift_amount, dest); } @@ -359,8 +536,7 @@ public: void urshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) { - if (src != dest) - move(src, dest); + move32IfNeeded(src, dest); urshift32(imm, dest); } @@ -371,7 +547,10 @@ public: void sub32(TrustedImm32 imm, RegisterID dest) { - m_assembler.subl_ir(imm.m_value, dest); + if (imm.m_value == 1) + m_assembler.dec_r(dest); + else + m_assembler.subl_ir(imm.m_value, dest); } void sub32(TrustedImm32 imm, Address address) @@ -405,9 +584,9 @@ public: void xor32(TrustedImm32 imm, RegisterID dest) { if (imm.m_value == -1) - m_assembler.notl_r(dest); + m_assembler.notl_r(dest); else - m_assembler.xorl_ir(imm.m_value, dest); + m_assembler.xorl_ir(imm.m_value, dest); } void xor32(RegisterID src, Address dest) @@ -427,27 +606,64 @@ public: else if (op1 == dest) xor32(op2, dest); else { - move(op2, dest); + move32IfNeeded(op2, dest); xor32(op1, dest); } } + void xor32(Address op1, RegisterID op2, RegisterID dest) + { + move32IfNeeded(op2, dest); + xor32(op1, dest); + } + + void xor32(RegisterID op1, Address op2, RegisterID dest) + { + move32IfNeeded(op1, dest); + xor32(op2, dest); + } + void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) { - move(src, dest); + move32IfNeeded(src, dest); xor32(imm, dest); } + void not32(RegisterID srcDest) + { + m_assembler.notl_r(srcDest); + } + + void not32(Address dest) + { + m_assembler.notl_m(dest.offset, dest.base); + } + void sqrtDouble(FPRegisterID src, FPRegisterID dst) { m_assembler.sqrtsd_rr(src, dst); } + void sqrtDouble(Address src, FPRegisterID dst) + { + m_assembler.sqrtsd_mr(src.offset, src.base, dst); + } + + void sqrtFloat(FPRegisterID src, FPRegisterID dst) + { + m_assembler.sqrtss_rr(src, dst); + } + + void sqrtFloat(Address src, FPRegisterID dst) + { + m_assembler.sqrtss_mr(src.offset, src.base, dst); + } + void absDouble(FPRegisterID src, FPRegisterID dst) { ASSERT(src != dst); static const double negativeZeroConstant = -0.0; - loadDouble(&negativeZeroConstant, dst); + loadDouble(TrustedImmPtr(&negativeZeroConstant), dst); m_assembler.andnpd_rr(src, dst); } @@ -455,10 +671,49 @@ public: { ASSERT(src != dst); static const double negativeZeroConstant = -0.0; - loadDouble(&negativeZeroConstant, dst); + loadDouble(TrustedImmPtr(&negativeZeroConstant), dst); m_assembler.xorpd_rr(src, dst); } + void ceilDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.roundsd_rr(src, dst, X86Assembler::RoundingType::TowardInfiniti); + } + + void ceilDouble(Address src, FPRegisterID dst) + { + m_assembler.roundsd_mr(src.offset, src.base, dst, X86Assembler::RoundingType::TowardInfiniti); + } + + void ceilFloat(FPRegisterID src, FPRegisterID dst) + { + m_assembler.roundss_rr(src, dst, X86Assembler::RoundingType::TowardInfiniti); + } + + void ceilFloat(Address src, FPRegisterID dst) + { + m_assembler.roundss_mr(src.offset, src.base, dst, X86Assembler::RoundingType::TowardInfiniti); + } + + void floorDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.roundsd_rr(src, dst, X86Assembler::RoundingType::TowardNegativeInfiniti); + } + + void floorDouble(Address src, FPRegisterID dst) + { + m_assembler.roundsd_mr(src.offset, src.base, dst, X86Assembler::RoundingType::TowardNegativeInfiniti); + } + + void floorFloat(FPRegisterID src, FPRegisterID dst) + { + m_assembler.roundss_rr(src, dst, X86Assembler::RoundingType::TowardNegativeInfiniti); + } + + void floorFloat(Address src, FPRegisterID dst) + { + m_assembler.roundss_mr(src.offset, src.base, dst, X86Assembler::RoundingType::TowardNegativeInfiniti); + } // Memory access operations: // @@ -524,15 +779,25 @@ public: m_assembler.movzbl_mr(address.offset, address.base, dest); } - void load8Signed(BaseIndex address, RegisterID dest) + void load8SignedExtendTo32(BaseIndex address, RegisterID dest) { m_assembler.movsbl_mr(address.offset, address.base, address.index, address.scale, dest); } - void load8Signed(ImplicitAddress address, RegisterID dest) + void load8SignedExtendTo32(ImplicitAddress address, RegisterID dest) { m_assembler.movsbl_mr(address.offset, address.base, dest); } + + void zeroExtend8To32(RegisterID src, RegisterID dest) + { + m_assembler.movzbl_rr(src, dest); + } + + void signExtend8To32(RegisterID src, RegisterID dest) + { + m_assembler.movsbl_rr(src, dest); + } void load16(BaseIndex address, RegisterID dest) { @@ -544,16 +809,26 @@ public: m_assembler.movzwl_mr(address.offset, address.base, dest); } - void load16Signed(BaseIndex address, RegisterID dest) + void load16SignedExtendTo32(BaseIndex address, RegisterID dest) { m_assembler.movswl_mr(address.offset, address.base, address.index, address.scale, dest); } - void load16Signed(Address address, RegisterID dest) + void load16SignedExtendTo32(Address address, RegisterID dest) { m_assembler.movswl_mr(address.offset, address.base, dest); } + void zeroExtend16To32(RegisterID src, RegisterID dest) + { + m_assembler.movzwl_rr(src, dest); + } + + void signExtend16To32(RegisterID src, RegisterID dest) + { + m_assembler.movswl_rr(src, dest); + } + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) { padBeforePatch(); @@ -581,18 +856,47 @@ public: m_assembler.movl_i32m(imm.m_value, address.offset, address.base, address.index, address.scale); } + void storeZero32(ImplicitAddress address) + { + store32(TrustedImm32(0), address); + } + + void storeZero32(BaseIndex address) + { + store32(TrustedImm32(0), address); + } + void store8(TrustedImm32 imm, Address address) { - ASSERT(-128 <= imm.m_value && imm.m_value < 128); - m_assembler.movb_i8m(imm.m_value, address.offset, address.base); + m_assembler.movb_i8m(static_cast<int8_t>(imm.m_value), address.offset, address.base); } void store8(TrustedImm32 imm, BaseIndex address) { - ASSERT(-128 <= imm.m_value && imm.m_value < 128); - m_assembler.movb_i8m(imm.m_value, address.offset, address.base, address.index, address.scale); + m_assembler.movb_i8m(static_cast<int8_t>(imm.m_value), address.offset, address.base, address.index, address.scale); } - + + static ALWAYS_INLINE RegisterID getUnusedRegister(BaseIndex address) + { + if (address.base != X86Registers::eax && address.index != X86Registers::eax) + return X86Registers::eax; + + if (address.base != X86Registers::ebx && address.index != X86Registers::ebx) + return X86Registers::ebx; + + ASSERT(address.base != X86Registers::ecx && address.index != X86Registers::ecx); + return X86Registers::ecx; + } + + static ALWAYS_INLINE RegisterID getUnusedRegister(Address address) + { + if (address.base != X86Registers::eax) + return X86Registers::eax; + + ASSERT(address.base != X86Registers::edx); + return X86Registers::edx; + } + void store8(RegisterID src, BaseIndex address) { #if CPU(X86) @@ -600,15 +904,7 @@ public: // esp..edi are mapped to the 'h' registers! if (src >= 4) { // Pick a temporary register. - RegisterID temp; - if (address.base != X86Registers::eax && address.index != X86Registers::eax) - temp = X86Registers::eax; - else if (address.base != X86Registers::ebx && address.index != X86Registers::ebx) - temp = X86Registers::ebx; - else { - ASSERT(address.base != X86Registers::ecx && address.index != X86Registers::ecx); - temp = X86Registers::ecx; - } + RegisterID temp = getUnusedRegister(address); // Swap to the temporary register to perform the store. swap(src, temp); @@ -619,6 +915,25 @@ public: #endif m_assembler.movb_rm(src, address.offset, address.base, address.index, address.scale); } + + void store8(RegisterID src, Address address) + { +#if CPU(X86) + // On 32-bit x86 we can only store from the first 4 registers; + // esp..edi are mapped to the 'h' registers! + if (src >= 4) { + // Pick a temporary register. + RegisterID temp = getUnusedRegister(address); + + // Swap to the temporary register to perform the store. + swap(src, temp); + m_assembler.movb_rm(temp, address.offset, address.base); + swap(src, temp); + return; + } +#endif + m_assembler.movb_rm(src, address.offset, address.base); + } void store16(RegisterID src, BaseIndex address) { @@ -627,16 +942,8 @@ public: // esp..edi are mapped to the 'h' registers! if (src >= 4) { // Pick a temporary register. - RegisterID temp; - if (address.base != X86Registers::eax && address.index != X86Registers::eax) - temp = X86Registers::eax; - else if (address.base != X86Registers::ebx && address.index != X86Registers::ebx) - temp = X86Registers::ebx; - else { - ASSERT(address.base != X86Registers::ecx && address.index != X86Registers::ecx); - temp = X86Registers::ecx; - } - + RegisterID temp = getUnusedRegister(address); + // Swap to the temporary register to perform the store. swap(src, temp); m_assembler.movw_rm(temp, address.offset, address.base, address.index, address.scale); @@ -647,6 +954,25 @@ public: m_assembler.movw_rm(src, address.offset, address.base, address.index, address.scale); } + void store16(RegisterID src, Address address) + { +#if CPU(X86) + // On 32-bit x86 we can only store from the first 4 registers; + // esp..edi are mapped to the 'h' registers! + if (src >= 4) { + // Pick a temporary register. + RegisterID temp = getUnusedRegister(address); + + // Swap to the temporary register to perform the store. + swap(src, temp); + m_assembler.movw_rm(temp, address.offset, address.base); + swap(src, temp); + return; + } +#endif + m_assembler.movw_rm(src, address.offset, address.base); + } + // Floating-point operation: // @@ -656,17 +982,17 @@ public: { ASSERT(isSSE2Present()); if (src != dest) - m_assembler.movsd_rr(src, dest); + m_assembler.movaps_rr(src, dest); } - void loadDouble(const void* address, FPRegisterID dest) + void loadDouble(TrustedImmPtr address, FPRegisterID dest) { #if CPU(X86) ASSERT(isSSE2Present()); - m_assembler.movsd_mr(address, dest); + m_assembler.movsd_mr(address.m_value, dest); #else - move(TrustedImmPtr(address), scratchRegister); - loadDouble(scratchRegister, dest); + move(address, scratchRegister()); + loadDouble(scratchRegister(), dest); #endif } @@ -675,12 +1001,19 @@ public: ASSERT(isSSE2Present()); m_assembler.movsd_mr(address.offset, address.base, dest); } - + void loadDouble(BaseIndex address, FPRegisterID dest) { ASSERT(isSSE2Present()); m_assembler.movsd_mr(address.offset, address.base, address.index, address.scale, dest); } + + void loadFloat(ImplicitAddress address, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.movss_mr(address.offset, address.base, dest); + } + void loadFloat(BaseIndex address, FPRegisterID dest) { ASSERT(isSSE2Present()); @@ -698,7 +1031,13 @@ public: ASSERT(isSSE2Present()); m_assembler.movsd_rm(src, address.offset, address.base, address.index, address.scale); } - + + void storeFloat(FPRegisterID src, ImplicitAddress address) + { + ASSERT(isSSE2Present()); + m_assembler.movss_rm(src, address.offset, address.base); + } + void storeFloat(FPRegisterID src, BaseIndex address) { ASSERT(isSSE2Present()); @@ -711,12 +1050,24 @@ public: m_assembler.cvtsd2ss_rr(src, dst); } + void convertDoubleToFloat(Address address, FPRegisterID dst) + { + ASSERT(isSSE2Present()); + m_assembler.cvtsd2ss_mr(address.offset, address.base, dst); + } + void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) { ASSERT(isSSE2Present()); m_assembler.cvtss2sd_rr(src, dst); } + void convertFloatToDouble(Address address, FPRegisterID dst) + { + ASSERT(isSSE2Present()); + m_assembler.cvtss2sd_mr(address.offset, address.base, dst); + } + void addDouble(FPRegisterID src, FPRegisterID dest) { ASSERT(isSSE2Present()); @@ -740,6 +1091,77 @@ public: m_assembler.addsd_mr(src.offset, src.base, dest); } + void addDouble(Address op1, FPRegisterID op2, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (op2 == dest) { + addDouble(op1, dest); + return; + } + + loadDouble(op1, dest); + addDouble(op2, dest); + } + + void addDouble(FPRegisterID op1, Address op2, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (op1 == dest) { + addDouble(op2, dest); + return; + } + + loadDouble(op2, dest); + addDouble(op1, dest); + } + + void addFloat(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.addss_rr(src, dest); + } + + void addFloat(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.addss_mr(src.offset, src.base, dest); + } + + void addFloat(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (op1 == dest) + addFloat(op2, dest); + else { + moveDouble(op2, dest); + addFloat(op1, dest); + } + } + + void addFloat(Address op1, FPRegisterID op2, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (op2 == dest) { + addFloat(op1, dest); + return; + } + + loadFloat(op1, dest); + addFloat(op2, dest); + } + + void addFloat(FPRegisterID op1, Address op2, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (op1 == dest) { + addFloat(op2, dest); + return; + } + + loadFloat(op2, dest); + addFloat(op1, dest); + } + void divDouble(FPRegisterID src, FPRegisterID dest) { ASSERT(isSSE2Present()); @@ -761,6 +1183,18 @@ public: m_assembler.divsd_mr(src.offset, src.base, dest); } + void divFloat(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.divss_rr(src, dest); + } + + void divFloat(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.divss_mr(src.offset, src.base, dest); + } + void subDouble(FPRegisterID src, FPRegisterID dest) { ASSERT(isSSE2Present()); @@ -782,6 +1216,18 @@ public: m_assembler.subsd_mr(src.offset, src.base, dest); } + void subFloat(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.subss_rr(src, dest); + } + + void subFloat(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.subss_mr(src.offset, src.base, dest); + } + void mulDouble(FPRegisterID src, FPRegisterID dest) { ASSERT(isSSE2Present()); @@ -805,6 +1251,134 @@ public: m_assembler.mulsd_mr(src.offset, src.base, dest); } + void mulDouble(Address op1, FPRegisterID op2, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (op2 == dest) { + mulDouble(op1, dest); + return; + } + loadDouble(op1, dest); + mulDouble(op2, dest); + } + + void mulDouble(FPRegisterID op1, Address op2, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (op1 == dest) { + mulDouble(op2, dest); + return; + } + loadDouble(op2, dest); + mulDouble(op1, dest); + } + + void mulFloat(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.mulss_rr(src, dest); + } + + void mulFloat(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.mulss_mr(src.offset, src.base, dest); + } + + void mulFloat(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (op1 == dest) + mulFloat(op2, dest); + else { + moveDouble(op2, dest); + mulFloat(op1, dest); + } + } + + void mulFloat(Address op1, FPRegisterID op2, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (op2 == dest) { + mulFloat(op1, dest); + return; + } + loadFloat(op1, dest); + mulFloat(op2, dest); + } + + void mulFloat(FPRegisterID op1, Address op2, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (op1 == dest) { + mulFloat(op2, dest); + return; + } + loadFloat(op2, dest); + mulFloat(op1, dest); + } + + void andDouble(FPRegisterID src, FPRegisterID dst) + { + // ANDPS is defined on 128bits and is shorter than ANDPD. + m_assembler.andps_rr(src, dst); + } + + void andDouble(FPRegisterID src1, FPRegisterID src2, FPRegisterID dst) + { + if (src1 == dst) + andDouble(src2, dst); + else { + moveDouble(src2, dst); + andDouble(src1, dst); + } + } + + void andFloat(FPRegisterID src, FPRegisterID dst) + { + m_assembler.andps_rr(src, dst); + } + + void andFloat(FPRegisterID src1, FPRegisterID src2, FPRegisterID dst) + { + if (src1 == dst) + andFloat(src2, dst); + else { + moveDouble(src2, dst); + andFloat(src1, dst); + } + } + + void xorDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.xorps_rr(src, dst); + } + + void xorDouble(FPRegisterID src1, FPRegisterID src2, FPRegisterID dst) + { + if (src1 == dst) + xorDouble(src2, dst); + else { + moveDouble(src2, dst); + xorDouble(src1, dst); + } + } + + void xorFloat(FPRegisterID src, FPRegisterID dst) + { + m_assembler.xorps_rr(src, dst); + } + + void xorFloat(FPRegisterID src1, FPRegisterID src2, FPRegisterID dst) + { + if (src1 == dst) + xorFloat(src2, dst); + else { + moveDouble(src2, dst); + xorFloat(src1, dst); + } + } + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) { ASSERT(isSSE2Present()); @@ -825,27 +1399,18 @@ public: m_assembler.ucomisd_rr(left, right); else m_assembler.ucomisd_rr(right, left); + return jumpAfterFloatingPointCompare(cond, left, right); + } - if (cond == DoubleEqual) { - if (left == right) - return Jump(m_assembler.jnp()); - Jump isUnordered(m_assembler.jp()); - Jump result = Jump(m_assembler.je()); - isUnordered.link(this); - return result; - } else if (cond == DoubleNotEqualOrUnordered) { - if (left == right) - return Jump(m_assembler.jp()); - Jump isUnordered(m_assembler.jp()); - Jump isEqual(m_assembler.je()); - isUnordered.link(this); - Jump result = jump(); - isEqual.link(this); - return result; - } + Jump branchFloat(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + ASSERT(isSSE2Present()); - ASSERT(!(cond & DoubleConditionBitSpecial)); - return Jump(m_assembler.jCC(static_cast<X86Assembler::Condition>(cond & ~DoubleConditionBits))); + if (cond & DoubleConditionBitInvert) + m_assembler.ucomiss_rr(left, right); + else + m_assembler.ucomiss_rr(right, left); + return jumpAfterFloatingPointCompare(cond, left, right); } // Truncates 'src' to an integer, and places the resulting 'dest'. @@ -860,13 +1425,6 @@ public: return branch32(branchType ? NotEqual : Equal, dest, TrustedImm32(0x80000000)); } - Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) - { - ASSERT(isSSE2Present()); - m_assembler.cvttsd2si_rr(src, dest); - return branch32(branchType ? GreaterThanOrEqual : LessThan, dest, TrustedImm32(0)); - } - void truncateDoubleToInt32(FPRegisterID src, RegisterID dest) { ASSERT(isSSE2Present()); @@ -891,8 +1449,17 @@ public: m_assembler.cvttsd2si_rr(src, dest); // If the result is zero, it might have been -0.0, and the double comparison won't catch this! +#if CPU(X86_64) + if (negZeroCheck) { + Jump valueIsNonZero = branchTest32(NonZero, dest); + m_assembler.movmskpd_rr(src, scratchRegister()); + failureCases.append(branchTest32(NonZero, scratchRegister(), TrustedImm32(1))); + valueIsNonZero.link(this); + } +#else if (negZeroCheck) failureCases.append(branchTest32(Zero, dest)); +#endif // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. convertInt32ToDouble(dest, fpTemp); @@ -901,6 +1468,11 @@ public: failureCases.append(m_assembler.jne()); } + void moveZeroToDouble(FPRegisterID reg) + { + m_assembler.xorps_rr(reg, reg); + } + Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) { ASSERT(isSSE2Present()); @@ -933,13 +1505,13 @@ public: m_assembler.por_rr(src, dst); } - void moveInt32ToPacked(RegisterID src, XMMRegisterID dst) + void move32ToFloat(RegisterID src, XMMRegisterID dst) { ASSERT(isSSE2Present()); m_assembler.movd_rr(src, dst); } - void movePackedToInt32(XMMRegisterID src, RegisterID dst) + void moveFloatTo32(XMMRegisterID src, RegisterID dst) { ASSERT(isSSE2Present()); m_assembler.movd_rr(src, dst); @@ -999,20 +1571,104 @@ public: void move(TrustedImmPtr imm, RegisterID dest) { - m_assembler.movq_i64r(imm.asIntptr(), dest); + if (!imm.m_value) + m_assembler.xorq_rr(dest, dest); + else + m_assembler.movq_i64r(imm.asIntptr(), dest); } void move(TrustedImm64 imm, RegisterID dest) { - m_assembler.movq_i64r(imm.m_value, dest); + if (!imm.m_value) + m_assembler.xorq_rr(dest, dest); + else + m_assembler.movq_i64r(imm.m_value, dest); } + void moveConditionallyDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right, RegisterID src, RegisterID dest) + { + ASSERT(isSSE2Present()); + + if (cond & DoubleConditionBitInvert) + m_assembler.ucomisd_rr(left, right); + else + m_assembler.ucomisd_rr(right, left); + moveConditionallyAfterFloatingPointCompare(cond, left, right, src, dest); + } + + void moveConditionallyDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right, RegisterID thenCase, RegisterID elseCase, RegisterID dest) + { + ASSERT(isSSE2Present()); + + if (thenCase != dest && elseCase != dest) { + move(elseCase, dest); + elseCase = dest; + } + + RegisterID src; + if (elseCase == dest) + src = thenCase; + else { + cond = invert(cond); + src = elseCase; + } + + if (cond & DoubleConditionBitInvert) + m_assembler.ucomisd_rr(left, right); + else + m_assembler.ucomisd_rr(right, left); + moveConditionallyAfterFloatingPointCompare(cond, left, right, src, dest); + } + + void moveConditionallyFloat(DoubleCondition cond, FPRegisterID left, FPRegisterID right, RegisterID src, RegisterID dest) + { + ASSERT(isSSE2Present()); + + if (cond & DoubleConditionBitInvert) + m_assembler.ucomiss_rr(left, right); + else + m_assembler.ucomiss_rr(right, left); + moveConditionallyAfterFloatingPointCompare(cond, left, right, src, dest); + } + + void moveConditionallyFloat(DoubleCondition cond, FPRegisterID left, FPRegisterID right, RegisterID thenCase, RegisterID elseCase, RegisterID dest) + { + ASSERT(isSSE2Present()); + + if (thenCase != dest && elseCase != dest) { + move(elseCase, dest); + elseCase = dest; + } + + RegisterID src; + if (elseCase == dest) + src = thenCase; + else { + cond = invert(cond); + src = elseCase; + } + + if (cond & DoubleConditionBitInvert) + m_assembler.ucomiss_rr(left, right); + else + m_assembler.ucomiss_rr(right, left); + moveConditionallyAfterFloatingPointCompare(cond, left, right, src, dest); + } + void swap(RegisterID reg1, RegisterID reg2) { if (reg1 != reg2) m_assembler.xchgq_rr(reg1, reg2); } + void signExtend32ToPtr(TrustedImm32 imm, RegisterID dest) + { + if (!imm.m_value) + m_assembler.xorq_rr(dest, dest); + else + m_assembler.mov_i32r(imm.m_value, dest); + } + void signExtend32ToPtr(RegisterID src, RegisterID dest) { m_assembler.movsxd_rr(src, dest); @@ -1022,6 +1678,11 @@ public: { m_assembler.movl_rr(src, dest); } + + void zeroExtend32ToPtr(TrustedImm32 src, RegisterID dest) + { + m_assembler.movl_i32r(src.m_value, dest); + } #else void move(RegisterID src, RegisterID dest) { @@ -1031,7 +1692,46 @@ public: void move(TrustedImmPtr imm, RegisterID dest) { - m_assembler.movl_i32r(imm.asIntptr(), dest); + if (!imm.m_value) + m_assembler.xorl_rr(dest, dest); + else + m_assembler.movl_i32r(imm.asIntptr(), dest); + } + + void moveConditionallyDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right, RegisterID src, RegisterID dest) + { + ASSERT(isSSE2Present()); + + if (cond & DoubleConditionBitInvert) + m_assembler.ucomisd_rr(left, right); + else + m_assembler.ucomisd_rr(right, left); + + if (cond == DoubleEqual) { + if (left == right) { + m_assembler.cmovnpl_rr(src, dest); + return; + } + + Jump isUnordered(m_assembler.jp()); + m_assembler.cmovel_rr(src, dest); + isUnordered.link(this); + return; + } + + if (cond == DoubleNotEqualOrUnordered) { + if (left == right) { + m_assembler.cmovpl_rr(src, dest); + return; + } + + m_assembler.cmovpl_rr(src, dest); + m_assembler.cmovnel_rr(src, dest); + return; + } + + ASSERT(!(cond & DoubleConditionBitSpecial)); + m_assembler.cmovl_rr(static_cast<X86Assembler::Condition>(cond & ~DoubleConditionBits), src, dest); } void swap(RegisterID reg1, RegisterID reg2) @@ -1051,6 +1751,190 @@ public: } #endif + void swap32(RegisterID src, RegisterID dest) + { + m_assembler.xchgl_rr(src, dest); + } + + void swap32(RegisterID src, Address dest) + { + m_assembler.xchgl_rm(src, dest.offset, dest.base); + } + + void moveConditionally32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID src, RegisterID dest) + { + m_assembler.cmpl_rr(right, left); + cmov(x86Condition(cond), src, dest); + } + + void moveConditionally32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID thenCase, RegisterID elseCase, RegisterID dest) + { + m_assembler.cmpl_rr(right, left); + + if (thenCase != dest && elseCase != dest) { + move(elseCase, dest); + elseCase = dest; + } + + if (elseCase == dest) + cmov(x86Condition(cond), thenCase, dest); + else + cmov(x86Condition(invert(cond)), elseCase, dest); + } + + void moveConditionally32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID thenCase, RegisterID elseCase, RegisterID dest) + { + if (!right.m_value) { + if (auto resultCondition = commuteCompareToZeroIntoTest(cond)) { + moveConditionallyTest32(*resultCondition, left, left, thenCase, elseCase, dest); + return; + } + } + + m_assembler.cmpl_ir(right.m_value, left); + + if (thenCase != dest && elseCase != dest) { + move(elseCase, dest); + elseCase = dest; + } + + if (elseCase == dest) + cmov(x86Condition(cond), thenCase, dest); + else + cmov(x86Condition(invert(cond)), elseCase, dest); + } + + void moveConditionallyTest32(ResultCondition cond, RegisterID testReg, RegisterID mask, RegisterID src, RegisterID dest) + { + m_assembler.testl_rr(testReg, mask); + cmov(x86Condition(cond), src, dest); + } + + void moveConditionallyTest32(ResultCondition cond, RegisterID left, RegisterID right, RegisterID thenCase, RegisterID elseCase, RegisterID dest) + { + ASSERT(isInvertible(cond)); + ASSERT_WITH_MESSAGE(cond != Overflow, "TEST does not set the Overflow Flag."); + + m_assembler.testl_rr(right, left); + + if (thenCase != dest && elseCase != dest) { + move(elseCase, dest); + elseCase = dest; + } + + if (elseCase == dest) + cmov(x86Condition(cond), thenCase, dest); + else + cmov(x86Condition(invert(cond)), elseCase, dest); + } + + void moveConditionallyTest32(ResultCondition cond, RegisterID testReg, TrustedImm32 mask, RegisterID src, RegisterID dest) + { + test32(testReg, mask); + cmov(x86Condition(cond), src, dest); + } + + void moveConditionallyTest32(ResultCondition cond, RegisterID testReg, TrustedImm32 mask, RegisterID thenCase, RegisterID elseCase, RegisterID dest) + { + ASSERT(isInvertible(cond)); + ASSERT_WITH_MESSAGE(cond != Overflow, "TEST does not set the Overflow Flag."); + + test32(testReg, mask); + + if (thenCase != dest && elseCase != dest) { + move(elseCase, dest); + elseCase = dest; + } + + if (elseCase == dest) + cmov(x86Condition(cond), thenCase, dest); + else + cmov(x86Condition(invert(cond)), elseCase, dest); + } + + template<typename LeftType, typename RightType> + void moveDoubleConditionally32(RelationalCondition cond, LeftType left, RightType right, FPRegisterID thenCase, FPRegisterID elseCase, FPRegisterID dest) + { + static_assert(!std::is_same<LeftType, FPRegisterID>::value && !std::is_same<RightType, FPRegisterID>::value, "One of the tested argument could be aliased on dest. Use moveDoubleConditionallyDouble()."); + + if (thenCase != dest && elseCase != dest) { + moveDouble(elseCase, dest); + elseCase = dest; + } + + if (elseCase == dest) { + Jump falseCase = branch32(invert(cond), left, right); + moveDouble(thenCase, dest); + falseCase.link(this); + } else { + Jump trueCase = branch32(cond, left, right); + moveDouble(elseCase, dest); + trueCase.link(this); + } + } + + template<typename TestType, typename MaskType> + void moveDoubleConditionallyTest32(ResultCondition cond, TestType test, MaskType mask, FPRegisterID thenCase, FPRegisterID elseCase, FPRegisterID dest) + { + static_assert(!std::is_same<TestType, FPRegisterID>::value && !std::is_same<MaskType, FPRegisterID>::value, "One of the tested argument could be aliased on dest. Use moveDoubleConditionallyDouble()."); + + if (elseCase == dest && isInvertible(cond)) { + Jump falseCase = branchTest32(invert(cond), test, mask); + moveDouble(thenCase, dest); + falseCase.link(this); + } else if (thenCase == dest) { + Jump trueCase = branchTest32(cond, test, mask); + moveDouble(elseCase, dest); + trueCase.link(this); + } + + Jump trueCase = branchTest32(cond, test, mask); + moveDouble(elseCase, dest); + Jump falseCase = jump(); + trueCase.link(this); + moveDouble(thenCase, dest); + falseCase.link(this); + } + + void moveDoubleConditionallyDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right, FPRegisterID thenCase, FPRegisterID elseCase, FPRegisterID dest) + { + if (elseCase == dest) { + Jump falseCase = branchDouble(invert(cond), left, right); + moveDouble(thenCase, dest); + falseCase.link(this); + } else if (thenCase == dest) { + Jump trueCase = branchDouble(cond, left, right); + moveDouble(elseCase, dest); + trueCase.link(this); + } else { + Jump trueCase = branchDouble(cond, left, right); + moveDouble(elseCase, dest); + Jump falseCase = jump(); + trueCase.link(this); + moveDouble(thenCase, dest); + falseCase.link(this); + } + } + + void moveDoubleConditionallyFloat(DoubleCondition cond, FPRegisterID left, FPRegisterID right, FPRegisterID thenCase, FPRegisterID elseCase, FPRegisterID dest) + { + if (elseCase == dest) { + Jump falseCase = branchFloat(invert(cond), left, right); + moveDouble(thenCase, dest); + falseCase.link(this); + } else if (thenCase == dest) { + Jump trueCase = branchFloat(cond, left, right); + moveDouble(elseCase, dest); + trueCase.link(this); + } else { + Jump trueCase = branchFloat(cond, left, right); + moveDouble(elseCase, dest); + Jump falseCase = jump(); + trueCase.link(this); + moveDouble(thenCase, dest); + falseCase.link(this); + } + } // Forwards / external control flow operations: // @@ -1085,10 +1969,12 @@ public: Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) { - if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) - m_assembler.testl_rr(left, left); - else - m_assembler.cmpl_ir(right.m_value, left); + if (!right.m_value) { + if (auto resultCondition = commuteCompareToZeroIntoTest(cond)) + return branchTest32(*resultCondition, left, left); + } + + m_assembler.cmpl_ir(right.m_value, left); return Jump(m_assembler.jCC(x86Condition(cond))); } @@ -1127,22 +2013,33 @@ public: return Jump(m_assembler.jCC(x86Condition(cond))); } - Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + void test32(RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) { - // if we are only interested in the low seven bits, this can be tested with a testb if (mask.m_value == -1) m_assembler.testl_rr(reg, reg); - else + else if (!(mask.m_value & ~0xff) && reg < X86Registers::esp) { // Using esp and greater as a byte register yields the upper half of the 16 bit registers ax, cx, dx and bx, e.g. esp, register 4, is actually ah. + if (mask.m_value == 0xff) + m_assembler.testb_rr(reg, reg); + else + m_assembler.testb_i8r(mask.m_value, reg); + } else m_assembler.testl_i32r(mask.m_value, reg); + } + + Jump branch(ResultCondition cond) + { return Jump(m_assembler.jCC(x86Condition(cond))); } + Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + test32(reg, mask); + return branch(cond); + } + Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) { - if (mask.m_value == -1) - m_assembler.cmpl_im(0, address.offset, address.base); - else - m_assembler.testl_i32m(mask.m_value, address.offset, address.base); + generateTest32(address, mask); return Jump(m_assembler.jCC(x86Condition(cond))); } @@ -1179,7 +2076,7 @@ public: Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) { - ASSERT(!(right.m_value & 0xFFFFFF00)); + ASSERT(std::numeric_limits<int8_t>::min() <= right.m_value && right.m_value <= std::numeric_limits<int8_t>::max()); m_assembler.cmpb_im(right.m_value, left.offset, left.base, left.index, left.scale); return Jump(m_assembler.jCC(x86Condition(cond))); @@ -1246,13 +2143,25 @@ public: { if (src1 == dest) return branchAdd32(cond, src2, dest); - move(src2, dest); + move32IfNeeded(src2, dest); + return branchAdd32(cond, src1, dest); + } + + Jump branchAdd32(ResultCondition cond, Address src1, RegisterID src2, RegisterID dest) + { + move32IfNeeded(src2, dest); return branchAdd32(cond, src1, dest); } + Jump branchAdd32(ResultCondition cond, RegisterID src1, Address src2, RegisterID dest) + { + move32IfNeeded(src1, dest); + return branchAdd32(cond, src2, dest); + } + Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) { - move(src, dest); + move32IfNeeded(src, dest); return branchAdd32(cond, imm, dest); } @@ -1272,7 +2181,7 @@ public: return Jump(m_assembler.jCC(x86Condition(cond))); } - Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) + Jump branchMul32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) { mul32(imm, src, dest); if (cond != Overflow) @@ -1284,7 +2193,7 @@ public: { if (src1 == dest) return branchMul32(cond, src2, dest); - move(src2, dest); + move32IfNeeded(src2, dest); return branchMul32(cond, src1, dest); } @@ -1323,13 +2232,13 @@ public: // B := A - B is invalid. ASSERT(src1 == dest || src2 != dest); - move(src1, dest); + move32IfNeeded(src1, dest); return branchSub32(cond, src2, dest); } Jump branchSub32(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) { - move(src1, dest); + move32IfNeeded(src1, dest); return branchSub32(cond, src2, dest); } @@ -1353,6 +2262,11 @@ public: m_assembler.int3(); } + Call nearTailCall() + { + return Call(m_assembler.jmp(), Call::LinkableNearTail); + } + Call nearCall() { return Call(m_assembler.call(), Call::LinkableNear); @@ -1387,10 +2301,14 @@ public: void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) { - if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) - m_assembler.testl_rr(left, left); - else - m_assembler.cmpl_ir(right.m_value, left); + if (!right.m_value) { + if (auto resultCondition = commuteCompareToZeroIntoTest(cond)) { + test32(*resultCondition, left, left, dest); + return; + } + } + + m_assembler.cmpl_ir(right.m_value, left); set32(x86Condition(cond), dest); } @@ -1410,23 +2328,121 @@ public: void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) { - if (mask.m_value == -1) - m_assembler.cmpl_im(0, address.offset, address.base); - else - m_assembler.testl_i32m(mask.m_value, address.offset, address.base); + generateTest32(address, mask); set32(x86Condition(cond), dest); } + void test32(ResultCondition cond, RegisterID reg, RegisterID mask, RegisterID dest) + { + m_assembler.testl_rr(reg, mask); + set32(x86Condition(cond), dest); + } + + void test32(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) + { + test32(reg, mask); + set32(x86Condition(cond), dest); + } + + void setCarry(RegisterID dest) + { + set32(X86Assembler::ConditionC, dest); + } + // Invert a relational condition, e.g. == becomes !=, < becomes >=, etc. static RelationalCondition invert(RelationalCondition cond) { return static_cast<RelationalCondition>(cond ^ 1); } + static DoubleCondition invert(DoubleCondition cond) + { + switch (cond) { + case DoubleEqual: + return DoubleNotEqualOrUnordered; + case DoubleNotEqual: + return DoubleEqualOrUnordered; + case DoubleGreaterThan: + return DoubleLessThanOrEqualOrUnordered; + case DoubleGreaterThanOrEqual: + return DoubleLessThanOrUnordered; + case DoubleLessThan: + return DoubleGreaterThanOrEqualOrUnordered; + case DoubleLessThanOrEqual: + return DoubleGreaterThanOrUnordered; + case DoubleEqualOrUnordered: + return DoubleNotEqual; + case DoubleNotEqualOrUnordered: + return DoubleEqual; + case DoubleGreaterThanOrUnordered: + return DoubleLessThanOrEqual; + case DoubleGreaterThanOrEqualOrUnordered: + return DoubleLessThan; + case DoubleLessThanOrUnordered: + return DoubleGreaterThanOrEqual; + case DoubleLessThanOrEqualOrUnordered: + return DoubleGreaterThan; + } + RELEASE_ASSERT_NOT_REACHED(); + return DoubleEqual; // make compiler happy + } + + static bool isInvertible(ResultCondition cond) + { + switch (cond) { + case Zero: + case NonZero: + case Signed: + case PositiveOrZero: + return true; + default: + return false; + } + } + + static ResultCondition invert(ResultCondition cond) + { + switch (cond) { + case Zero: + return NonZero; + case NonZero: + return Zero; + case Signed: + return PositiveOrZero; + case PositiveOrZero: + return Signed; + default: + RELEASE_ASSERT_NOT_REACHED(); + return Zero; // Make compiler happy for release builds. + } + } + + static Optional<ResultCondition> commuteCompareToZeroIntoTest(RelationalCondition cond) + { + switch (cond) { + case Equal: + return Zero; + case NotEqual: + return NonZero; + case LessThan: + return Signed; + case GreaterThanOrEqual: + return PositiveOrZero; + break; + default: + return Nullopt; + } + } + void nop() { m_assembler.nop(); } + + void memoryFence() + { + m_assembler.mfence(); + } static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) { @@ -1438,6 +2454,46 @@ public: return X86Assembler::maxJumpReplacementSize(); } + static bool supportsFloatingPointRounding() + { + if (s_sse4_1CheckState == CPUIDCheckState::NotChecked) { + int flags = 0; +#if COMPILER(MSVC) + int cpuInfo[4]; + __cpuid(cpuInfo, 0x1); + flags = cpuInfo[2]; +#elif COMPILER(GCC_OR_CLANG) +#if CPU(X86_64) + asm ( + "movl $0x1, %%eax;" + "cpuid;" + "movl %%ecx, %0;" + : "=g" (flags) + : + : "%eax", "%ebx", "%ecx", "%edx" + ); +#else + asm ( + "movl $0x1, %%eax;" + "pushl %%ebx;" + "cpuid;" + "popl %%ebx;" + "movl %%ecx, %0;" + : "=g" (flags) + : + : "%eax", "%ecx", "%edx" + ); +#endif +#endif // COMPILER(GCC_OR_CLANG) + s_sse4_1CheckState = (flags & (1 << 19)) ? CPUIDCheckState::Set : CPUIDCheckState::Clear; + } + return s_sse4_1CheckState == CPUIDCheckState::Set; + } + +#if ENABLE(MASM_PROBE) + void probe(ProbeFunction, void* arg1, void* arg2); +#endif // ENABLE(MASM_PROBE) + protected: X86Assembler::Condition x86Condition(RelationalCondition cond) { @@ -1466,11 +2522,150 @@ protected: m_assembler.movzbl_rr(dest, dest); } + void cmov(X86Assembler::Condition cond, RegisterID src, RegisterID dest) + { +#if CPU(X86_64) + m_assembler.cmovq_rr(cond, src, dest); +#else + m_assembler.cmovl_rr(cond, src, dest); +#endif + } + + static bool supportsLZCNT() + { + if (s_lzcntCheckState == CPUIDCheckState::NotChecked) { + int flags = 0; +#if COMPILER(MSVC) + int cpuInfo[4]; + __cpuid(cpuInfo, 0x80000001); + flags = cpuInfo[2]; +#elif COMPILER(GCC_OR_CLANG) +#if CPU(X86_64) + asm ( + "movl $0x80000001, %%eax;" + "cpuid;" + "movl %%ecx, %0;" + : "=g" (flags) + : + : "%eax", "%ebx", "%ecx", "%edx" + ); +#else + asm ( + "movl $0x80000001, %%eax;" + "pushl %%ebx;" + "cpuid;" + "popl %%ebx;" + "movl %%ecx, %0;" + : "=g" (flags) + : + : "%eax", "%ecx", "%edx" + ); +#endif +#endif // COMPILER(GCC_OR_CLANG) + s_lzcntCheckState = (flags & 0x20) ? CPUIDCheckState::Set : CPUIDCheckState::Clear; + } + return s_lzcntCheckState == CPUIDCheckState::Set; + } + private: // Only MacroAssemblerX86 should be using the following method; SSE2 is always available on // x86_64, and clients & subclasses of MacroAssembler should be using 'supportsFloatingPoint()'. friend class MacroAssemblerX86; + ALWAYS_INLINE void generateTest32(Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + if (mask.m_value == -1) + m_assembler.cmpl_im(0, address.offset, address.base); + else if (!(mask.m_value & ~0xff)) + m_assembler.testb_im(mask.m_value, address.offset, address.base); + else if (!(mask.m_value & ~0xff00)) + m_assembler.testb_im(mask.m_value >> 8, address.offset + 1, address.base); + else if (!(mask.m_value & ~0xff0000)) + m_assembler.testb_im(mask.m_value >> 16, address.offset + 2, address.base); + else if (!(mask.m_value & ~0xff000000)) + m_assembler.testb_im(mask.m_value >> 24, address.offset + 3, address.base); + else + m_assembler.testl_i32m(mask.m_value, address.offset, address.base); + } + + // If lzcnt is not available, use this after BSR + // to count the leading zeros. + void clz32AfterBsr(RegisterID dst) + { + Jump srcIsNonZero = m_assembler.jCC(x86Condition(NonZero)); + move(TrustedImm32(32), dst); + + Jump skipNonZeroCase = jump(); + srcIsNonZero.link(this); + xor32(TrustedImm32(0x1f), dst); + skipNonZeroCase.link(this); + } + + Jump jumpAfterFloatingPointCompare(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + if (cond == DoubleEqual) { + if (left == right) + return Jump(m_assembler.jnp()); + Jump isUnordered(m_assembler.jp()); + Jump result = Jump(m_assembler.je()); + isUnordered.link(this); + return result; + } + if (cond == DoubleNotEqualOrUnordered) { + if (left == right) + return Jump(m_assembler.jp()); + Jump isUnordered(m_assembler.jp()); + Jump isEqual(m_assembler.je()); + isUnordered.link(this); + Jump result = jump(); + isEqual.link(this); + return result; + } + + ASSERT(!(cond & DoubleConditionBitSpecial)); + return Jump(m_assembler.jCC(static_cast<X86Assembler::Condition>(cond & ~DoubleConditionBits))); + } + + // The 32bit Move does not need the REX byte for low registers, making it shorter. + // Use this if the top bits are irrelevant because they will be reset by the next instruction. + void move32IfNeeded(RegisterID src, RegisterID dest) + { + if (src == dest) + return; + m_assembler.movl_rr(src, dest); + } + +#if CPU(X86_64) + void moveConditionallyAfterFloatingPointCompare(DoubleCondition cond, FPRegisterID left, FPRegisterID right, RegisterID src, RegisterID dest) + { + if (cond == DoubleEqual) { + if (left == right) { + m_assembler.cmovnpq_rr(src, dest); + return; + } + + Jump isUnordered(m_assembler.jp()); + m_assembler.cmoveq_rr(src, dest); + isUnordered.link(this); + return; + } + + if (cond == DoubleNotEqualOrUnordered) { + if (left == right) { + m_assembler.cmovpq_rr(src, dest); + return; + } + + m_assembler.cmovpq_rr(src, dest); + m_assembler.cmovneq_rr(src, dest); + return; + } + + ASSERT(!(cond & DoubleConditionBitSpecial)); + cmov(static_cast<X86Assembler::Condition>(cond & ~DoubleConditionBits), src, dest); + } +#endif + #if CPU(X86) #if OS(MAC_OS_X) @@ -1500,7 +2695,7 @@ private: cpuid; mov flags, edx; } -#elif COMPILER(GCC) +#elif COMPILER(GCC_OR_CLANG) asm ( "movl $0x1, %%eax;" "pushl %%ebx;" @@ -1534,6 +2729,14 @@ private: } #endif + + enum class CPUIDCheckState { + NotChecked, + Clear, + Set + }; + static CPUIDCheckState s_sse4_1CheckState; + static CPUIDCheckState s_lzcntCheckState; }; } // namespace JSC |