OLD | NEW |
1 // Copyright 2011 the V8 project authors. All rights reserved. | 1 // Copyright 2011 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 1762 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1773 // If length is not zero, "tos_" contains a non-zero value ==> true. | 1773 // If length is not zero, "tos_" contains a non-zero value ==> true. |
1774 __ Ret(); | 1774 __ Ret(); |
1775 | 1775 |
1776 // Return 0 in "tos_" for false . | 1776 // Return 0 in "tos_" for false . |
1777 __ bind(&false_result); | 1777 __ bind(&false_result); |
1778 __ mov(tos_, Operand(0, RelocInfo::NONE)); | 1778 __ mov(tos_, Operand(0, RelocInfo::NONE)); |
1779 __ Ret(); | 1779 __ Ret(); |
1780 } | 1780 } |
1781 | 1781 |
1782 | 1782 |
1783 const char* GenericBinaryOpStub::GetName() { | |
1784 if (name_ != NULL) return name_; | |
1785 const int len = 100; | |
1786 name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray(len); | |
1787 if (name_ == NULL) return "OOM"; | |
1788 const char* op_name = Token::Name(op_); | |
1789 const char* overwrite_name; | |
1790 switch (mode_) { | |
1791 case NO_OVERWRITE: overwrite_name = "Alloc"; break; | |
1792 case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break; | |
1793 case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break; | |
1794 default: overwrite_name = "UnknownOverwrite"; break; | |
1795 } | |
1796 | |
1797 OS::SNPrintF(Vector<char>(name_, len), | |
1798 "GenericBinaryOpStub_%s_%s%s_%s", | |
1799 op_name, | |
1800 overwrite_name, | |
1801 specialized_on_rhs_ ? "_ConstantRhs" : "", | |
1802 BinaryOpIC::GetName(runtime_operands_type_)); | |
1803 return name_; | |
1804 } | |
1805 | |
1806 | |
1807 // We fall into this code if the operands were Smis, but the result was | |
1808 // not (eg. overflow). We branch into this code (to the not_smi label) if | |
1809 // the operands were not both Smi. The operands are in r0 and r1. In order | |
1810 // to call the C-implemented binary fp operation routines we need to end up | |
1811 // with the double precision floating point operands in r0 and r1 (for the | |
1812 // value in r1) and r2 and r3 (for the value in r0). | |
1813 void GenericBinaryOpStub::HandleBinaryOpSlowCases( | |
1814 MacroAssembler* masm, | |
1815 Label* not_smi, | |
1816 Register lhs, | |
1817 Register rhs, | |
1818 const Builtins::JavaScript& builtin) { | |
1819 Label slow, slow_reverse, do_the_call; | |
1820 bool use_fp_registers = | |
1821 CpuFeatures::IsSupported(VFP3) && | |
1822 Token::MOD != op_; | |
1823 | |
1824 ASSERT((lhs.is(r0) && rhs.is(r1)) || (lhs.is(r1) && rhs.is(r0))); | |
1825 Register heap_number_map = r6; | |
1826 | |
1827 if (ShouldGenerateSmiCode()) { | |
1828 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); | |
1829 | |
1830 // Smi-smi case (overflow). | |
1831 // Since both are Smis there is no heap number to overwrite, so allocate. | |
1832 // The new heap number is in r5. r3 and r7 are scratch. | |
1833 __ AllocateHeapNumber( | |
1834 r5, r3, r7, heap_number_map, lhs.is(r0) ? &slow_reverse : &slow); | |
1835 | |
1836 // If we have floating point hardware, inline ADD, SUB, MUL, and DIV, | |
1837 // using registers d7 and d6 for the double values. | |
1838 if (CpuFeatures::IsSupported(VFP3)) { | |
1839 CpuFeatures::Scope scope(VFP3); | |
1840 __ mov(r7, Operand(rhs, ASR, kSmiTagSize)); | |
1841 __ vmov(s15, r7); | |
1842 __ vcvt_f64_s32(d7, s15); | |
1843 __ mov(r7, Operand(lhs, ASR, kSmiTagSize)); | |
1844 __ vmov(s13, r7); | |
1845 __ vcvt_f64_s32(d6, s13); | |
1846 if (!use_fp_registers) { | |
1847 __ vmov(r2, r3, d7); | |
1848 __ vmov(r0, r1, d6); | |
1849 } | |
1850 } else { | |
1851 // Write Smi from rhs to r3 and r2 in double format. r9 is scratch. | |
1852 __ mov(r7, Operand(rhs)); | |
1853 ConvertToDoubleStub stub1(r3, r2, r7, r9); | |
1854 __ push(lr); | |
1855 __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET); | |
1856 // Write Smi from lhs to r1 and r0 in double format. r9 is scratch. | |
1857 __ mov(r7, Operand(lhs)); | |
1858 ConvertToDoubleStub stub2(r1, r0, r7, r9); | |
1859 __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET); | |
1860 __ pop(lr); | |
1861 } | |
1862 __ jmp(&do_the_call); // Tail call. No return. | |
1863 } | |
1864 | |
1865 // We branch here if at least one of r0 and r1 is not a Smi. | |
1866 __ bind(not_smi); | |
1867 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); | |
1868 | |
1869 // After this point we have the left hand side in r1 and the right hand side | |
1870 // in r0. | |
1871 if (lhs.is(r0)) { | |
1872 __ Swap(r0, r1, ip); | |
1873 } | |
1874 | |
1875 // The type transition also calculates the answer. | |
1876 bool generate_code_to_calculate_answer = true; | |
1877 | |
1878 if (ShouldGenerateFPCode()) { | |
1879 // DIV has neither SmiSmi fast code nor specialized slow code. | |
1880 // So don't try to patch a DIV Stub. | |
1881 if (runtime_operands_type_ == BinaryOpIC::DEFAULT) { | |
1882 switch (op_) { | |
1883 case Token::ADD: | |
1884 case Token::SUB: | |
1885 case Token::MUL: | |
1886 GenerateTypeTransition(masm); // Tail call. | |
1887 generate_code_to_calculate_answer = false; | |
1888 break; | |
1889 | |
1890 case Token::DIV: | |
1891 // DIV has neither SmiSmi fast code nor specialized slow code. | |
1892 // So don't try to patch a DIV Stub. | |
1893 break; | |
1894 | |
1895 default: | |
1896 break; | |
1897 } | |
1898 } | |
1899 | |
1900 if (generate_code_to_calculate_answer) { | |
1901 Label r0_is_smi, r1_is_smi, finished_loading_r0, finished_loading_r1; | |
1902 if (mode_ == NO_OVERWRITE) { | |
1903 // In the case where there is no chance of an overwritable float we may | |
1904 // as well do the allocation immediately while r0 and r1 are untouched. | |
1905 __ AllocateHeapNumber(r5, r3, r7, heap_number_map, &slow); | |
1906 } | |
1907 | |
1908 // Move r0 to a double in r2-r3. | |
1909 __ tst(r0, Operand(kSmiTagMask)); | |
1910 __ b(eq, &r0_is_smi); // It's a Smi so don't check it's a heap number. | |
1911 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset)); | |
1912 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); | |
1913 __ cmp(r4, heap_number_map); | |
1914 __ b(ne, &slow); | |
1915 if (mode_ == OVERWRITE_RIGHT) { | |
1916 __ mov(r5, Operand(r0)); // Overwrite this heap number. | |
1917 } | |
1918 if (use_fp_registers) { | |
1919 CpuFeatures::Scope scope(VFP3); | |
1920 // Load the double from tagged HeapNumber r0 to d7. | |
1921 __ sub(r7, r0, Operand(kHeapObjectTag)); | |
1922 __ vldr(d7, r7, HeapNumber::kValueOffset); | |
1923 } else { | |
1924 // Calling convention says that second double is in r2 and r3. | |
1925 __ Ldrd(r2, r3, FieldMemOperand(r0, HeapNumber::kValueOffset)); | |
1926 } | |
1927 __ jmp(&finished_loading_r0); | |
1928 __ bind(&r0_is_smi); | |
1929 if (mode_ == OVERWRITE_RIGHT) { | |
1930 // We can't overwrite a Smi so get address of new heap number into r5. | |
1931 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow); | |
1932 } | |
1933 | |
1934 if (CpuFeatures::IsSupported(VFP3)) { | |
1935 CpuFeatures::Scope scope(VFP3); | |
1936 // Convert smi in r0 to double in d7. | |
1937 __ mov(r7, Operand(r0, ASR, kSmiTagSize)); | |
1938 __ vmov(s15, r7); | |
1939 __ vcvt_f64_s32(d7, s15); | |
1940 if (!use_fp_registers) { | |
1941 __ vmov(r2, r3, d7); | |
1942 } | |
1943 } else { | |
1944 // Write Smi from r0 to r3 and r2 in double format. | |
1945 __ mov(r7, Operand(r0)); | |
1946 ConvertToDoubleStub stub3(r3, r2, r7, r4); | |
1947 __ push(lr); | |
1948 __ Call(stub3.GetCode(), RelocInfo::CODE_TARGET); | |
1949 __ pop(lr); | |
1950 } | |
1951 | |
1952 // HEAP_NUMBERS stub is slower than GENERIC on a pair of smis. | |
1953 // r0 is known to be a smi. If r1 is also a smi then switch to GENERIC. | |
1954 Label r1_is_not_smi; | |
1955 if ((runtime_operands_type_ == BinaryOpIC::HEAP_NUMBERS) && | |
1956 HasSmiSmiFastPath()) { | |
1957 __ tst(r1, Operand(kSmiTagMask)); | |
1958 __ b(ne, &r1_is_not_smi); | |
1959 GenerateTypeTransition(masm); // Tail call. | |
1960 } | |
1961 | |
1962 __ bind(&finished_loading_r0); | |
1963 | |
1964 // Move r1 to a double in r0-r1. | |
1965 __ tst(r1, Operand(kSmiTagMask)); | |
1966 __ b(eq, &r1_is_smi); // It's a Smi so don't check it's a heap number. | |
1967 __ bind(&r1_is_not_smi); | |
1968 __ ldr(r4, FieldMemOperand(r1, HeapNumber::kMapOffset)); | |
1969 __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); | |
1970 __ cmp(r4, heap_number_map); | |
1971 __ b(ne, &slow); | |
1972 if (mode_ == OVERWRITE_LEFT) { | |
1973 __ mov(r5, Operand(r1)); // Overwrite this heap number. | |
1974 } | |
1975 if (use_fp_registers) { | |
1976 CpuFeatures::Scope scope(VFP3); | |
1977 // Load the double from tagged HeapNumber r1 to d6. | |
1978 __ sub(r7, r1, Operand(kHeapObjectTag)); | |
1979 __ vldr(d6, r7, HeapNumber::kValueOffset); | |
1980 } else { | |
1981 // Calling convention says that first double is in r0 and r1. | |
1982 __ Ldrd(r0, r1, FieldMemOperand(r1, HeapNumber::kValueOffset)); | |
1983 } | |
1984 __ jmp(&finished_loading_r1); | |
1985 __ bind(&r1_is_smi); | |
1986 if (mode_ == OVERWRITE_LEFT) { | |
1987 // We can't overwrite a Smi so get address of new heap number into r5. | |
1988 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow); | |
1989 } | |
1990 | |
1991 if (CpuFeatures::IsSupported(VFP3)) { | |
1992 CpuFeatures::Scope scope(VFP3); | |
1993 // Convert smi in r1 to double in d6. | |
1994 __ mov(r7, Operand(r1, ASR, kSmiTagSize)); | |
1995 __ vmov(s13, r7); | |
1996 __ vcvt_f64_s32(d6, s13); | |
1997 if (!use_fp_registers) { | |
1998 __ vmov(r0, r1, d6); | |
1999 } | |
2000 } else { | |
2001 // Write Smi from r1 to r1 and r0 in double format. | |
2002 __ mov(r7, Operand(r1)); | |
2003 ConvertToDoubleStub stub4(r1, r0, r7, r9); | |
2004 __ push(lr); | |
2005 __ Call(stub4.GetCode(), RelocInfo::CODE_TARGET); | |
2006 __ pop(lr); | |
2007 } | |
2008 | |
2009 __ bind(&finished_loading_r1); | |
2010 } | |
2011 | |
2012 if (generate_code_to_calculate_answer || do_the_call.is_linked()) { | |
2013 __ bind(&do_the_call); | |
2014 // If we are inlining the operation using VFP3 instructions for | |
2015 // add, subtract, multiply, or divide, the arguments are in d6 and d7. | |
2016 if (use_fp_registers) { | |
2017 CpuFeatures::Scope scope(VFP3); | |
2018 // ARMv7 VFP3 instructions to implement | |
2019 // double precision, add, subtract, multiply, divide. | |
2020 | |
2021 if (Token::MUL == op_) { | |
2022 __ vmul(d5, d6, d7); | |
2023 } else if (Token::DIV == op_) { | |
2024 __ vdiv(d5, d6, d7); | |
2025 } else if (Token::ADD == op_) { | |
2026 __ vadd(d5, d6, d7); | |
2027 } else if (Token::SUB == op_) { | |
2028 __ vsub(d5, d6, d7); | |
2029 } else { | |
2030 UNREACHABLE(); | |
2031 } | |
2032 __ sub(r0, r5, Operand(kHeapObjectTag)); | |
2033 __ vstr(d5, r0, HeapNumber::kValueOffset); | |
2034 __ add(r0, r0, Operand(kHeapObjectTag)); | |
2035 __ Ret(); | |
2036 } else { | |
2037 // If we did not inline the operation, then the arguments are in: | |
2038 // r0: Left value (least significant part of mantissa). | |
2039 // r1: Left value (sign, exponent, top of mantissa). | |
2040 // r2: Right value (least significant part of mantissa). | |
2041 // r3: Right value (sign, exponent, top of mantissa). | |
2042 // r5: Address of heap number for result. | |
2043 | |
2044 __ push(lr); // For later. | |
2045 __ PrepareCallCFunction(4, r4); // Two doubles count as 4 arguments. | |
2046 // Call C routine that may not cause GC or other trouble. r5 is callee | |
2047 // save. | |
2048 __ CallCFunction( | |
2049 ExternalReference::double_fp_operation(op_, masm->isolate()), 4); | |
2050 // Store answer in the overwritable heap number. | |
2051 #if !defined(USE_ARM_EABI) | |
2052 // Double returned in fp coprocessor register 0 and 1, encoded as | |
2053 // register cr8. Offsets must be divisible by 4 for coprocessor so we | |
2054 // need to substract the tag from r5. | |
2055 __ sub(r4, r5, Operand(kHeapObjectTag)); | |
2056 __ stc(p1, cr8, MemOperand(r4, HeapNumber::kValueOffset)); | |
2057 #else | |
2058 // Double returned in registers 0 and 1. | |
2059 __ Strd(r0, r1, FieldMemOperand(r5, HeapNumber::kValueOffset)); | |
2060 #endif | |
2061 __ mov(r0, Operand(r5)); | |
2062 // And we are done. | |
2063 __ pop(pc); | |
2064 } | |
2065 } | |
2066 } | |
2067 | |
2068 if (!generate_code_to_calculate_answer && | |
2069 !slow_reverse.is_linked() && | |
2070 !slow.is_linked()) { | |
2071 return; | |
2072 } | |
2073 | |
2074 if (lhs.is(r0)) { | |
2075 __ b(&slow); | |
2076 __ bind(&slow_reverse); | |
2077 __ Swap(r0, r1, ip); | |
2078 } | |
2079 | |
2080 heap_number_map = no_reg; // Don't use this any more from here on. | |
2081 | |
2082 // We jump to here if something goes wrong (one param is not a number of any | |
2083 // sort or new-space allocation fails). | |
2084 __ bind(&slow); | |
2085 | |
2086 // Push arguments to the stack | |
2087 __ Push(r1, r0); | |
2088 | |
2089 if (Token::ADD == op_) { | |
2090 // Test for string arguments before calling runtime. | |
2091 // r1 : first argument | |
2092 // r0 : second argument | |
2093 // sp[0] : second argument | |
2094 // sp[4] : first argument | |
2095 | |
2096 Label not_strings, not_string1, string1, string1_smi2; | |
2097 __ tst(r1, Operand(kSmiTagMask)); | |
2098 __ b(eq, ¬_string1); | |
2099 __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE); | |
2100 __ b(ge, ¬_string1); | |
2101 | |
2102 // First argument is a a string, test second. | |
2103 __ tst(r0, Operand(kSmiTagMask)); | |
2104 __ b(eq, &string1_smi2); | |
2105 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE); | |
2106 __ b(ge, &string1); | |
2107 | |
2108 // First and second argument are strings. | |
2109 StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB); | |
2110 __ TailCallStub(&string_add_stub); | |
2111 | |
2112 __ bind(&string1_smi2); | |
2113 // First argument is a string, second is a smi. Try to lookup the number | |
2114 // string for the smi in the number string cache. | |
2115 NumberToStringStub::GenerateLookupNumberStringCache( | |
2116 masm, r0, r2, r4, r5, r6, true, &string1); | |
2117 | |
2118 // Replace second argument on stack and tailcall string add stub to make | |
2119 // the result. | |
2120 __ str(r2, MemOperand(sp, 0)); | |
2121 __ TailCallStub(&string_add_stub); | |
2122 | |
2123 // Only first argument is a string. | |
2124 __ bind(&string1); | |
2125 __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_JS); | |
2126 | |
2127 // First argument was not a string, test second. | |
2128 __ bind(¬_string1); | |
2129 __ tst(r0, Operand(kSmiTagMask)); | |
2130 __ b(eq, ¬_strings); | |
2131 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE); | |
2132 __ b(ge, ¬_strings); | |
2133 | |
2134 // Only second argument is a string. | |
2135 __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_JS); | |
2136 | |
2137 __ bind(¬_strings); | |
2138 } | |
2139 | |
2140 __ InvokeBuiltin(builtin, JUMP_JS); // Tail call. No return. | |
2141 } | |
2142 | |
2143 | |
2144 // For bitwise ops where the inputs are not both Smis we here try to determine | |
2145 // whether both inputs are either Smis or at least heap numbers that can be | |
2146 // represented by a 32 bit signed value. We truncate towards zero as required | |
2147 // by the ES spec. If this is the case we do the bitwise op and see if the | |
2148 // result is a Smi. If so, great, otherwise we try to find a heap number to | |
2149 // write the answer into (either by allocating or by overwriting). | |
2150 // On entry the operands are in lhs and rhs. On exit the answer is in r0. | |
2151 void GenericBinaryOpStub::HandleNonSmiBitwiseOp(MacroAssembler* masm, | |
2152 Register lhs, | |
2153 Register rhs) { | |
2154 Label slow, result_not_a_smi; | |
2155 Label rhs_is_smi, lhs_is_smi; | |
2156 Label done_checking_rhs, done_checking_lhs; | |
2157 | |
2158 Register heap_number_map = r6; | |
2159 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); | |
2160 | |
2161 __ tst(lhs, Operand(kSmiTagMask)); | |
2162 __ b(eq, &lhs_is_smi); // It's a Smi so don't check it's a heap number. | |
2163 __ ldr(r4, FieldMemOperand(lhs, HeapNumber::kMapOffset)); | |
2164 __ cmp(r4, heap_number_map); | |
2165 __ b(ne, &slow); | |
2166 __ ConvertToInt32(lhs, r3, r5, r4, d0, &slow); | |
2167 __ jmp(&done_checking_lhs); | |
2168 __ bind(&lhs_is_smi); | |
2169 __ mov(r3, Operand(lhs, ASR, 1)); | |
2170 __ bind(&done_checking_lhs); | |
2171 | |
2172 __ tst(rhs, Operand(kSmiTagMask)); | |
2173 __ b(eq, &rhs_is_smi); // It's a Smi so don't check it's a heap number. | |
2174 __ ldr(r4, FieldMemOperand(rhs, HeapNumber::kMapOffset)); | |
2175 __ cmp(r4, heap_number_map); | |
2176 __ b(ne, &slow); | |
2177 __ ConvertToInt32(rhs, r2, r5, r4, d0, &slow); | |
2178 __ jmp(&done_checking_rhs); | |
2179 __ bind(&rhs_is_smi); | |
2180 __ mov(r2, Operand(rhs, ASR, 1)); | |
2181 __ bind(&done_checking_rhs); | |
2182 | |
2183 ASSERT(((lhs.is(r0) && rhs.is(r1)) || (lhs.is(r1) && rhs.is(r0)))); | |
2184 | |
2185 // r0 and r1: Original operands (Smi or heap numbers). | |
2186 // r2 and r3: Signed int32 operands. | |
2187 switch (op_) { | |
2188 case Token::BIT_OR: __ orr(r2, r2, Operand(r3)); break; | |
2189 case Token::BIT_XOR: __ eor(r2, r2, Operand(r3)); break; | |
2190 case Token::BIT_AND: __ and_(r2, r2, Operand(r3)); break; | |
2191 case Token::SAR: | |
2192 // Use only the 5 least significant bits of the shift count. | |
2193 __ and_(r2, r2, Operand(0x1f)); | |
2194 __ mov(r2, Operand(r3, ASR, r2)); | |
2195 break; | |
2196 case Token::SHR: | |
2197 // Use only the 5 least significant bits of the shift count. | |
2198 __ and_(r2, r2, Operand(0x1f)); | |
2199 __ mov(r2, Operand(r3, LSR, r2), SetCC); | |
2200 // SHR is special because it is required to produce a positive answer. | |
2201 // The code below for writing into heap numbers isn't capable of writing | |
2202 // the register as an unsigned int so we go to slow case if we hit this | |
2203 // case. | |
2204 if (CpuFeatures::IsSupported(VFP3)) { | |
2205 __ b(mi, &result_not_a_smi); | |
2206 } else { | |
2207 __ b(mi, &slow); | |
2208 } | |
2209 break; | |
2210 case Token::SHL: | |
2211 // Use only the 5 least significant bits of the shift count. | |
2212 __ and_(r2, r2, Operand(0x1f)); | |
2213 __ mov(r2, Operand(r3, LSL, r2)); | |
2214 break; | |
2215 default: UNREACHABLE(); | |
2216 } | |
2217 // check that the *signed* result fits in a smi | |
2218 __ add(r3, r2, Operand(0x40000000), SetCC); | |
2219 __ b(mi, &result_not_a_smi); | |
2220 __ mov(r0, Operand(r2, LSL, kSmiTagSize)); | |
2221 __ Ret(); | |
2222 | |
2223 Label have_to_allocate, got_a_heap_number; | |
2224 __ bind(&result_not_a_smi); | |
2225 switch (mode_) { | |
2226 case OVERWRITE_RIGHT: { | |
2227 __ tst(rhs, Operand(kSmiTagMask)); | |
2228 __ b(eq, &have_to_allocate); | |
2229 __ mov(r5, Operand(rhs)); | |
2230 break; | |
2231 } | |
2232 case OVERWRITE_LEFT: { | |
2233 __ tst(lhs, Operand(kSmiTagMask)); | |
2234 __ b(eq, &have_to_allocate); | |
2235 __ mov(r5, Operand(lhs)); | |
2236 break; | |
2237 } | |
2238 case NO_OVERWRITE: { | |
2239 // Get a new heap number in r5. r4 and r7 are scratch. | |
2240 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow); | |
2241 } | |
2242 default: break; | |
2243 } | |
2244 __ bind(&got_a_heap_number); | |
2245 // r2: Answer as signed int32. | |
2246 // r5: Heap number to write answer into. | |
2247 | |
2248 // Nothing can go wrong now, so move the heap number to r0, which is the | |
2249 // result. | |
2250 __ mov(r0, Operand(r5)); | |
2251 | |
2252 if (CpuFeatures::IsSupported(VFP3)) { | |
2253 // Convert the int32 in r2 to the heap number in r0. r3 is corrupted. | |
2254 CpuFeatures::Scope scope(VFP3); | |
2255 __ vmov(s0, r2); | |
2256 if (op_ == Token::SHR) { | |
2257 __ vcvt_f64_u32(d0, s0); | |
2258 } else { | |
2259 __ vcvt_f64_s32(d0, s0); | |
2260 } | |
2261 __ sub(r3, r0, Operand(kHeapObjectTag)); | |
2262 __ vstr(d0, r3, HeapNumber::kValueOffset); | |
2263 __ Ret(); | |
2264 } else { | |
2265 // Tail call that writes the int32 in r2 to the heap number in r0, using | |
2266 // r3 as scratch. r0 is preserved and returned. | |
2267 WriteInt32ToHeapNumberStub stub(r2, r0, r3); | |
2268 __ TailCallStub(&stub); | |
2269 } | |
2270 | |
2271 if (mode_ != NO_OVERWRITE) { | |
2272 __ bind(&have_to_allocate); | |
2273 // Get a new heap number in r5. r4 and r7 are scratch. | |
2274 __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow); | |
2275 __ jmp(&got_a_heap_number); | |
2276 } | |
2277 | |
2278 // If all else failed then we go to the runtime system. | |
2279 __ bind(&slow); | |
2280 __ Push(lhs, rhs); // Restore stack. | |
2281 switch (op_) { | |
2282 case Token::BIT_OR: | |
2283 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS); | |
2284 break; | |
2285 case Token::BIT_AND: | |
2286 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS); | |
2287 break; | |
2288 case Token::BIT_XOR: | |
2289 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS); | |
2290 break; | |
2291 case Token::SAR: | |
2292 __ InvokeBuiltin(Builtins::SAR, JUMP_JS); | |
2293 break; | |
2294 case Token::SHR: | |
2295 __ InvokeBuiltin(Builtins::SHR, JUMP_JS); | |
2296 break; | |
2297 case Token::SHL: | |
2298 __ InvokeBuiltin(Builtins::SHL, JUMP_JS); | |
2299 break; | |
2300 default: | |
2301 UNREACHABLE(); | |
2302 } | |
2303 } | |
2304 | |
2305 | |
2306 | |
2307 | |
2308 // This function takes the known int in a register for the cases | |
2309 // where it doesn't know a good trick, and may deliver | |
2310 // a result that needs shifting. | |
2311 static void MultiplyByKnownIntInStub( | |
2312 MacroAssembler* masm, | |
2313 Register result, | |
2314 Register source, | |
2315 Register known_int_register, // Smi tagged. | |
2316 int known_int, | |
2317 int* required_shift) { // Including Smi tag shift | |
2318 switch (known_int) { | |
2319 case 3: | |
2320 __ add(result, source, Operand(source, LSL, 1)); | |
2321 *required_shift = 1; | |
2322 break; | |
2323 case 5: | |
2324 __ add(result, source, Operand(source, LSL, 2)); | |
2325 *required_shift = 1; | |
2326 break; | |
2327 case 6: | |
2328 __ add(result, source, Operand(source, LSL, 1)); | |
2329 *required_shift = 2; | |
2330 break; | |
2331 case 7: | |
2332 __ rsb(result, source, Operand(source, LSL, 3)); | |
2333 *required_shift = 1; | |
2334 break; | |
2335 case 9: | |
2336 __ add(result, source, Operand(source, LSL, 3)); | |
2337 *required_shift = 1; | |
2338 break; | |
2339 case 10: | |
2340 __ add(result, source, Operand(source, LSL, 2)); | |
2341 *required_shift = 2; | |
2342 break; | |
2343 default: | |
2344 ASSERT(!IsPowerOf2(known_int)); // That would be very inefficient. | |
2345 __ mul(result, source, known_int_register); | |
2346 *required_shift = 0; | |
2347 } | |
2348 } | |
2349 | |
2350 | |
2351 // This uses versions of the sum-of-digits-to-see-if-a-number-is-divisible-by-3 | |
2352 // trick. See http://en.wikipedia.org/wiki/Divisibility_rule | |
2353 // Takes the sum of the digits base (mask + 1) repeatedly until we have a | |
2354 // number from 0 to mask. On exit the 'eq' condition flags are set if the | |
2355 // answer is exactly the mask. | |
2356 void IntegerModStub::DigitSum(MacroAssembler* masm, | |
2357 Register lhs, | |
2358 int mask, | |
2359 int shift, | |
2360 Label* entry) { | |
2361 ASSERT(mask > 0); | |
2362 ASSERT(mask <= 0xff); // This ensures we don't need ip to use it. | |
2363 Label loop; | |
2364 __ bind(&loop); | |
2365 __ and_(ip, lhs, Operand(mask)); | |
2366 __ add(lhs, ip, Operand(lhs, LSR, shift)); | |
2367 __ bind(entry); | |
2368 __ cmp(lhs, Operand(mask)); | |
2369 __ b(gt, &loop); | |
2370 } | |
2371 | |
2372 | |
2373 void IntegerModStub::DigitSum(MacroAssembler* masm, | |
2374 Register lhs, | |
2375 Register scratch, | |
2376 int mask, | |
2377 int shift1, | |
2378 int shift2, | |
2379 Label* entry) { | |
2380 ASSERT(mask > 0); | |
2381 ASSERT(mask <= 0xff); // This ensures we don't need ip to use it. | |
2382 Label loop; | |
2383 __ bind(&loop); | |
2384 __ bic(scratch, lhs, Operand(mask)); | |
2385 __ and_(ip, lhs, Operand(mask)); | |
2386 __ add(lhs, ip, Operand(lhs, LSR, shift1)); | |
2387 __ add(lhs, lhs, Operand(scratch, LSR, shift2)); | |
2388 __ bind(entry); | |
2389 __ cmp(lhs, Operand(mask)); | |
2390 __ b(gt, &loop); | |
2391 } | |
2392 | |
2393 | |
2394 // Splits the number into two halves (bottom half has shift bits). The top | |
2395 // half is subtracted from the bottom half. If the result is negative then | |
2396 // rhs is added. | |
2397 void IntegerModStub::ModGetInRangeBySubtraction(MacroAssembler* masm, | |
2398 Register lhs, | |
2399 int shift, | |
2400 int rhs) { | |
2401 int mask = (1 << shift) - 1; | |
2402 __ and_(ip, lhs, Operand(mask)); | |
2403 __ sub(lhs, ip, Operand(lhs, LSR, shift), SetCC); | |
2404 __ add(lhs, lhs, Operand(rhs), LeaveCC, mi); | |
2405 } | |
2406 | |
2407 | |
2408 void IntegerModStub::ModReduce(MacroAssembler* masm, | |
2409 Register lhs, | |
2410 int max, | |
2411 int denominator) { | |
2412 int limit = denominator; | |
2413 while (limit * 2 <= max) limit *= 2; | |
2414 while (limit >= denominator) { | |
2415 __ cmp(lhs, Operand(limit)); | |
2416 __ sub(lhs, lhs, Operand(limit), LeaveCC, ge); | |
2417 limit >>= 1; | |
2418 } | |
2419 } | |
2420 | |
2421 | |
2422 void IntegerModStub::ModAnswer(MacroAssembler* masm, | |
2423 Register result, | |
2424 Register shift_distance, | |
2425 Register mask_bits, | |
2426 Register sum_of_digits) { | |
2427 __ add(result, mask_bits, Operand(sum_of_digits, LSL, shift_distance)); | |
2428 __ Ret(); | |
2429 } | |
2430 | |
2431 | |
2432 // See comment for class. | |
2433 void IntegerModStub::Generate(MacroAssembler* masm) { | |
2434 __ mov(lhs_, Operand(lhs_, LSR, shift_distance_)); | |
2435 __ bic(odd_number_, odd_number_, Operand(1)); | |
2436 __ mov(odd_number_, Operand(odd_number_, LSL, 1)); | |
2437 // We now have (odd_number_ - 1) * 2 in the register. | |
2438 // Build a switch out of branches instead of data because it avoids | |
2439 // having to teach the assembler about intra-code-object pointers | |
2440 // that are not in relative branch instructions. | |
2441 Label mod3, mod5, mod7, mod9, mod11, mod13, mod15, mod17, mod19; | |
2442 Label mod21, mod23, mod25; | |
2443 { Assembler::BlockConstPoolScope block_const_pool(masm); | |
2444 __ add(pc, pc, Operand(odd_number_)); | |
2445 // When you read pc it is always 8 ahead, but when you write it you always | |
2446 // write the actual value. So we put in two nops to take up the slack. | |
2447 __ nop(); | |
2448 __ nop(); | |
2449 __ b(&mod3); | |
2450 __ b(&mod5); | |
2451 __ b(&mod7); | |
2452 __ b(&mod9); | |
2453 __ b(&mod11); | |
2454 __ b(&mod13); | |
2455 __ b(&mod15); | |
2456 __ b(&mod17); | |
2457 __ b(&mod19); | |
2458 __ b(&mod21); | |
2459 __ b(&mod23); | |
2460 __ b(&mod25); | |
2461 } | |
2462 | |
2463 // For each denominator we find a multiple that is almost only ones | |
2464 // when expressed in binary. Then we do the sum-of-digits trick for | |
2465 // that number. If the multiple is not 1 then we have to do a little | |
2466 // more work afterwards to get the answer into the 0-denominator-1 | |
2467 // range. | |
2468 DigitSum(masm, lhs_, 3, 2, &mod3); // 3 = b11. | |
2469 __ sub(lhs_, lhs_, Operand(3), LeaveCC, eq); | |
2470 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_); | |
2471 | |
2472 DigitSum(masm, lhs_, 0xf, 4, &mod5); // 5 * 3 = b1111. | |
2473 ModGetInRangeBySubtraction(masm, lhs_, 2, 5); | |
2474 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_); | |
2475 | |
2476 DigitSum(masm, lhs_, 7, 3, &mod7); // 7 = b111. | |
2477 __ sub(lhs_, lhs_, Operand(7), LeaveCC, eq); | |
2478 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_); | |
2479 | |
2480 DigitSum(masm, lhs_, 0x3f, 6, &mod9); // 7 * 9 = b111111. | |
2481 ModGetInRangeBySubtraction(masm, lhs_, 3, 9); | |
2482 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_); | |
2483 | |
2484 DigitSum(masm, lhs_, r5, 0x3f, 6, 3, &mod11); // 5 * 11 = b110111. | |
2485 ModReduce(masm, lhs_, 0x3f, 11); | |
2486 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_); | |
2487 | |
2488 DigitSum(masm, lhs_, r5, 0xff, 8, 5, &mod13); // 19 * 13 = b11110111. | |
2489 ModReduce(masm, lhs_, 0xff, 13); | |
2490 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_); | |
2491 | |
2492 DigitSum(masm, lhs_, 0xf, 4, &mod15); // 15 = b1111. | |
2493 __ sub(lhs_, lhs_, Operand(15), LeaveCC, eq); | |
2494 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_); | |
2495 | |
2496 DigitSum(masm, lhs_, 0xff, 8, &mod17); // 15 * 17 = b11111111. | |
2497 ModGetInRangeBySubtraction(masm, lhs_, 4, 17); | |
2498 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_); | |
2499 | |
2500 DigitSum(masm, lhs_, r5, 0xff, 8, 5, &mod19); // 13 * 19 = b11110111. | |
2501 ModReduce(masm, lhs_, 0xff, 19); | |
2502 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_); | |
2503 | |
2504 DigitSum(masm, lhs_, 0x3f, 6, &mod21); // 3 * 21 = b111111. | |
2505 ModReduce(masm, lhs_, 0x3f, 21); | |
2506 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_); | |
2507 | |
2508 DigitSum(masm, lhs_, r5, 0xff, 8, 7, &mod23); // 11 * 23 = b11111101. | |
2509 ModReduce(masm, lhs_, 0xff, 23); | |
2510 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_); | |
2511 | |
2512 DigitSum(masm, lhs_, r5, 0x7f, 7, 6, &mod25); // 5 * 25 = b1111101. | |
2513 ModReduce(masm, lhs_, 0x7f, 25); | |
2514 ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_); | |
2515 } | |
2516 | |
2517 | |
2518 void GenericBinaryOpStub::Generate(MacroAssembler* masm) { | |
2519 // lhs_ : x | |
2520 // rhs_ : y | |
2521 // r0 : result | |
2522 | |
2523 Register result = r0; | |
2524 Register lhs = lhs_; | |
2525 Register rhs = rhs_; | |
2526 | |
2527 // This code can't cope with other register allocations yet. | |
2528 ASSERT(result.is(r0) && | |
2529 ((lhs.is(r0) && rhs.is(r1)) || | |
2530 (lhs.is(r1) && rhs.is(r0)))); | |
2531 | |
2532 Register smi_test_reg = r7; | |
2533 Register scratch = r9; | |
2534 | |
2535 // All ops need to know whether we are dealing with two Smis. Set up | |
2536 // smi_test_reg to tell us that. | |
2537 if (ShouldGenerateSmiCode()) { | |
2538 __ orr(smi_test_reg, lhs, Operand(rhs)); | |
2539 } | |
2540 | |
2541 switch (op_) { | |
2542 case Token::ADD: { | |
2543 Label not_smi; | |
2544 // Fast path. | |
2545 if (ShouldGenerateSmiCode()) { | |
2546 STATIC_ASSERT(kSmiTag == 0); // Adjust code below. | |
2547 __ tst(smi_test_reg, Operand(kSmiTagMask)); | |
2548 __ b(ne, ¬_smi); | |
2549 __ add(r0, r1, Operand(r0), SetCC); // Add y optimistically. | |
2550 // Return if no overflow. | |
2551 __ Ret(vc); | |
2552 __ sub(r0, r0, Operand(r1)); // Revert optimistic add. | |
2553 } | |
2554 HandleBinaryOpSlowCases(masm, ¬_smi, lhs, rhs, Builtins::ADD); | |
2555 break; | |
2556 } | |
2557 | |
2558 case Token::SUB: { | |
2559 Label not_smi; | |
2560 // Fast path. | |
2561 if (ShouldGenerateSmiCode()) { | |
2562 STATIC_ASSERT(kSmiTag == 0); // Adjust code below. | |
2563 __ tst(smi_test_reg, Operand(kSmiTagMask)); | |
2564 __ b(ne, ¬_smi); | |
2565 if (lhs.is(r1)) { | |
2566 __ sub(r0, r1, Operand(r0), SetCC); // Subtract y optimistically. | |
2567 // Return if no overflow. | |
2568 __ Ret(vc); | |
2569 __ sub(r0, r1, Operand(r0)); // Revert optimistic subtract. | |
2570 } else { | |
2571 __ sub(r0, r0, Operand(r1), SetCC); // Subtract y optimistically. | |
2572 // Return if no overflow. | |
2573 __ Ret(vc); | |
2574 __ add(r0, r0, Operand(r1)); // Revert optimistic subtract. | |
2575 } | |
2576 } | |
2577 HandleBinaryOpSlowCases(masm, ¬_smi, lhs, rhs, Builtins::SUB); | |
2578 break; | |
2579 } | |
2580 | |
2581 case Token::MUL: { | |
2582 Label not_smi, slow; | |
2583 if (ShouldGenerateSmiCode()) { | |
2584 STATIC_ASSERT(kSmiTag == 0); // adjust code below | |
2585 __ tst(smi_test_reg, Operand(kSmiTagMask)); | |
2586 Register scratch2 = smi_test_reg; | |
2587 smi_test_reg = no_reg; | |
2588 __ b(ne, ¬_smi); | |
2589 // Remove tag from one operand (but keep sign), so that result is Smi. | |
2590 __ mov(ip, Operand(rhs, ASR, kSmiTagSize)); | |
2591 // Do multiplication | |
2592 // scratch = lower 32 bits of ip * lhs. | |
2593 __ smull(scratch, scratch2, lhs, ip); | |
2594 // Go slow on overflows (overflow bit is not set). | |
2595 __ mov(ip, Operand(scratch, ASR, 31)); | |
2596 // No overflow if higher 33 bits are identical. | |
2597 __ cmp(ip, Operand(scratch2)); | |
2598 __ b(ne, &slow); | |
2599 // Go slow on zero result to handle -0. | |
2600 __ tst(scratch, Operand(scratch)); | |
2601 __ mov(result, Operand(scratch), LeaveCC, ne); | |
2602 __ Ret(ne); | |
2603 // We need -0 if we were multiplying a negative number with 0 to get 0. | |
2604 // We know one of them was zero. | |
2605 __ add(scratch2, rhs, Operand(lhs), SetCC); | |
2606 __ mov(result, Operand(Smi::FromInt(0)), LeaveCC, pl); | |
2607 __ Ret(pl); // Return Smi 0 if the non-zero one was positive. | |
2608 // Slow case. We fall through here if we multiplied a negative number | |
2609 // with 0, because that would mean we should produce -0. | |
2610 __ bind(&slow); | |
2611 } | |
2612 HandleBinaryOpSlowCases(masm, ¬_smi, lhs, rhs, Builtins::MUL); | |
2613 break; | |
2614 } | |
2615 | |
2616 case Token::DIV: | |
2617 case Token::MOD: { | |
2618 Label not_smi; | |
2619 if (ShouldGenerateSmiCode() && specialized_on_rhs_) { | |
2620 Label lhs_is_unsuitable; | |
2621 __ JumpIfNotSmi(lhs, ¬_smi); | |
2622 if (IsPowerOf2(constant_rhs_)) { | |
2623 if (op_ == Token::MOD) { | |
2624 __ and_(rhs, | |
2625 lhs, | |
2626 Operand(0x80000000u | ((constant_rhs_ << kSmiTagSize) - 1)), | |
2627 SetCC); | |
2628 // We now have the answer, but if the input was negative we also | |
2629 // have the sign bit. Our work is done if the result is | |
2630 // positive or zero: | |
2631 if (!rhs.is(r0)) { | |
2632 __ mov(r0, rhs, LeaveCC, pl); | |
2633 } | |
2634 __ Ret(pl); | |
2635 // A mod of a negative left hand side must return a negative number. | |
2636 // Unfortunately if the answer is 0 then we must return -0. And we | |
2637 // already optimistically trashed rhs so we may need to restore it. | |
2638 __ eor(rhs, rhs, Operand(0x80000000u), SetCC); | |
2639 // Next two instructions are conditional on the answer being -0. | |
2640 __ mov(rhs, Operand(Smi::FromInt(constant_rhs_)), LeaveCC, eq); | |
2641 __ b(eq, &lhs_is_unsuitable); | |
2642 // We need to subtract the dividend. Eg. -3 % 4 == -3. | |
2643 __ sub(result, rhs, Operand(Smi::FromInt(constant_rhs_))); | |
2644 } else { | |
2645 ASSERT(op_ == Token::DIV); | |
2646 __ tst(lhs, | |
2647 Operand(0x80000000u | ((constant_rhs_ << kSmiTagSize) - 1))); | |
2648 __ b(ne, &lhs_is_unsuitable); // Go slow on negative or remainder. | |
2649 int shift = 0; | |
2650 int d = constant_rhs_; | |
2651 while ((d & 1) == 0) { | |
2652 d >>= 1; | |
2653 shift++; | |
2654 } | |
2655 __ mov(r0, Operand(lhs, LSR, shift)); | |
2656 __ bic(r0, r0, Operand(kSmiTagMask)); | |
2657 } | |
2658 } else { | |
2659 // Not a power of 2. | |
2660 __ tst(lhs, Operand(0x80000000u)); | |
2661 __ b(ne, &lhs_is_unsuitable); | |
2662 // Find a fixed point reciprocal of the divisor so we can divide by | |
2663 // multiplying. | |
2664 double divisor = 1.0 / constant_rhs_; | |
2665 int shift = 32; | |
2666 double scale = 4294967296.0; // 1 << 32. | |
2667 uint32_t mul; | |
2668 // Maximise the precision of the fixed point reciprocal. | |
2669 while (true) { | |
2670 mul = static_cast<uint32_t>(scale * divisor); | |
2671 if (mul >= 0x7fffffff) break; | |
2672 scale *= 2.0; | |
2673 shift++; | |
2674 } | |
2675 mul++; | |
2676 Register scratch2 = smi_test_reg; | |
2677 smi_test_reg = no_reg; | |
2678 __ mov(scratch2, Operand(mul)); | |
2679 __ umull(scratch, scratch2, scratch2, lhs); | |
2680 __ mov(scratch2, Operand(scratch2, LSR, shift - 31)); | |
2681 // scratch2 is lhs / rhs. scratch2 is not Smi tagged. | |
2682 // rhs is still the known rhs. rhs is Smi tagged. | |
2683 // lhs is still the unkown lhs. lhs is Smi tagged. | |
2684 int required_scratch_shift = 0; // Including the Smi tag shift of 1. | |
2685 // scratch = scratch2 * rhs. | |
2686 MultiplyByKnownIntInStub(masm, | |
2687 scratch, | |
2688 scratch2, | |
2689 rhs, | |
2690 constant_rhs_, | |
2691 &required_scratch_shift); | |
2692 // scratch << required_scratch_shift is now the Smi tagged rhs * | |
2693 // (lhs / rhs) where / indicates integer division. | |
2694 if (op_ == Token::DIV) { | |
2695 __ cmp(lhs, Operand(scratch, LSL, required_scratch_shift)); | |
2696 __ b(ne, &lhs_is_unsuitable); // There was a remainder. | |
2697 __ mov(result, Operand(scratch2, LSL, kSmiTagSize)); | |
2698 } else { | |
2699 ASSERT(op_ == Token::MOD); | |
2700 __ sub(result, lhs, Operand(scratch, LSL, required_scratch_shift)); | |
2701 } | |
2702 } | |
2703 __ Ret(); | |
2704 __ bind(&lhs_is_unsuitable); | |
2705 } else if (op_ == Token::MOD && | |
2706 runtime_operands_type_ != BinaryOpIC::HEAP_NUMBERS && | |
2707 runtime_operands_type_ != BinaryOpIC::STRINGS) { | |
2708 // Do generate a bit of smi code for modulus even though the default for | |
2709 // modulus is not to do it, but as the ARM processor has no coprocessor | |
2710 // support for modulus checking for smis makes sense. We can handle | |
2711 // 1 to 25 times any power of 2. This covers over half the numbers from | |
2712 // 1 to 100 including all of the first 25. (Actually the constants < 10 | |
2713 // are handled above by reciprocal multiplication. We only get here for | |
2714 // those cases if the right hand side is not a constant or for cases | |
2715 // like 192 which is 3*2^6 and ends up in the 3 case in the integer mod | |
2716 // stub.) | |
2717 Label slow; | |
2718 Label not_power_of_2; | |
2719 ASSERT(!ShouldGenerateSmiCode()); | |
2720 STATIC_ASSERT(kSmiTag == 0); // Adjust code below. | |
2721 // Check for two positive smis. | |
2722 __ orr(smi_test_reg, lhs, Operand(rhs)); | |
2723 __ tst(smi_test_reg, Operand(0x80000000u | kSmiTagMask)); | |
2724 __ b(ne, &slow); | |
2725 // Check that rhs is a power of two and not zero. | |
2726 Register mask_bits = r3; | |
2727 __ sub(scratch, rhs, Operand(1), SetCC); | |
2728 __ b(mi, &slow); | |
2729 __ and_(mask_bits, rhs, Operand(scratch), SetCC); | |
2730 __ b(ne, ¬_power_of_2); | |
2731 // Calculate power of two modulus. | |
2732 __ and_(result, lhs, Operand(scratch)); | |
2733 __ Ret(); | |
2734 | |
2735 __ bind(¬_power_of_2); | |
2736 __ eor(scratch, scratch, Operand(mask_bits)); | |
2737 // At least two bits are set in the modulus. The high one(s) are in | |
2738 // mask_bits and the low one is scratch + 1. | |
2739 __ and_(mask_bits, scratch, Operand(lhs)); | |
2740 Register shift_distance = scratch; | |
2741 scratch = no_reg; | |
2742 | |
2743 // The rhs consists of a power of 2 multiplied by some odd number. | |
2744 // The power-of-2 part we handle by putting the corresponding bits | |
2745 // from the lhs in the mask_bits register, and the power in the | |
2746 // shift_distance register. Shift distance is never 0 due to Smi | |
2747 // tagging. | |
2748 __ CountLeadingZeros(r4, shift_distance, shift_distance); | |
2749 __ rsb(shift_distance, r4, Operand(32)); | |
2750 | |
2751 // Now we need to find out what the odd number is. The last bit is | |
2752 // always 1. | |
2753 Register odd_number = r4; | |
2754 __ mov(odd_number, Operand(rhs, LSR, shift_distance)); | |
2755 __ cmp(odd_number, Operand(25)); | |
2756 __ b(gt, &slow); | |
2757 | |
2758 IntegerModStub stub( | |
2759 result, shift_distance, odd_number, mask_bits, lhs, r5); | |
2760 __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET); // Tail call. | |
2761 | |
2762 __ bind(&slow); | |
2763 } | |
2764 HandleBinaryOpSlowCases( | |
2765 masm, | |
2766 ¬_smi, | |
2767 lhs, | |
2768 rhs, | |
2769 op_ == Token::MOD ? Builtins::MOD : Builtins::DIV); | |
2770 break; | |
2771 } | |
2772 | |
2773 case Token::BIT_OR: | |
2774 case Token::BIT_AND: | |
2775 case Token::BIT_XOR: | |
2776 case Token::SAR: | |
2777 case Token::SHR: | |
2778 case Token::SHL: { | |
2779 Label slow; | |
2780 STATIC_ASSERT(kSmiTag == 0); // adjust code below | |
2781 __ tst(smi_test_reg, Operand(kSmiTagMask)); | |
2782 __ b(ne, &slow); | |
2783 Register scratch2 = smi_test_reg; | |
2784 smi_test_reg = no_reg; | |
2785 switch (op_) { | |
2786 case Token::BIT_OR: __ orr(result, rhs, Operand(lhs)); break; | |
2787 case Token::BIT_AND: __ and_(result, rhs, Operand(lhs)); break; | |
2788 case Token::BIT_XOR: __ eor(result, rhs, Operand(lhs)); break; | |
2789 case Token::SAR: | |
2790 // Remove tags from right operand. | |
2791 __ GetLeastBitsFromSmi(scratch2, rhs, 5); | |
2792 __ mov(result, Operand(lhs, ASR, scratch2)); | |
2793 // Smi tag result. | |
2794 __ bic(result, result, Operand(kSmiTagMask)); | |
2795 break; | |
2796 case Token::SHR: | |
2797 // Remove tags from operands. We can't do this on a 31 bit number | |
2798 // because then the 0s get shifted into bit 30 instead of bit 31. | |
2799 __ mov(scratch, Operand(lhs, ASR, kSmiTagSize)); // x | |
2800 __ GetLeastBitsFromSmi(scratch2, rhs, 5); | |
2801 __ mov(scratch, Operand(scratch, LSR, scratch2)); | |
2802 // Unsigned shift is not allowed to produce a negative number, so | |
2803 // check the sign bit and the sign bit after Smi tagging. | |
2804 __ tst(scratch, Operand(0xc0000000)); | |
2805 __ b(ne, &slow); | |
2806 // Smi tag result. | |
2807 __ mov(result, Operand(scratch, LSL, kSmiTagSize)); | |
2808 break; | |
2809 case Token::SHL: | |
2810 // Remove tags from operands. | |
2811 __ mov(scratch, Operand(lhs, ASR, kSmiTagSize)); // x | |
2812 __ GetLeastBitsFromSmi(scratch2, rhs, 5); | |
2813 __ mov(scratch, Operand(scratch, LSL, scratch2)); | |
2814 // Check that the signed result fits in a Smi. | |
2815 __ add(scratch2, scratch, Operand(0x40000000), SetCC); | |
2816 __ b(mi, &slow); | |
2817 __ mov(result, Operand(scratch, LSL, kSmiTagSize)); | |
2818 break; | |
2819 default: UNREACHABLE(); | |
2820 } | |
2821 __ Ret(); | |
2822 __ bind(&slow); | |
2823 HandleNonSmiBitwiseOp(masm, lhs, rhs); | |
2824 break; | |
2825 } | |
2826 | |
2827 default: UNREACHABLE(); | |
2828 } | |
2829 // This code should be unreachable. | |
2830 __ stop("Unreachable"); | |
2831 | |
2832 // Generate an unreachable reference to the DEFAULT stub so that it can be | |
2833 // found at the end of this stub when clearing ICs at GC. | |
2834 // TODO(kaznacheev): Check performance impact and get rid of this. | |
2835 if (runtime_operands_type_ != BinaryOpIC::DEFAULT) { | |
2836 GenericBinaryOpStub uninit(MinorKey(), BinaryOpIC::DEFAULT); | |
2837 __ CallStub(&uninit); | |
2838 } | |
2839 } | |
2840 | |
2841 | |
2842 void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) { | |
2843 Label get_result; | |
2844 | |
2845 __ Push(r1, r0); | |
2846 | |
2847 __ mov(r2, Operand(Smi::FromInt(MinorKey()))); | |
2848 __ mov(r1, Operand(Smi::FromInt(op_))); | |
2849 __ mov(r0, Operand(Smi::FromInt(runtime_operands_type_))); | |
2850 __ Push(r2, r1, r0); | |
2851 | |
2852 __ TailCallExternalReference( | |
2853 ExternalReference(IC_Utility(IC::kBinaryOp_Patch), masm->isolate()), | |
2854 5, | |
2855 1); | |
2856 } | |
2857 | |
2858 | |
2859 Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) { | |
2860 GenericBinaryOpStub stub(key, type_info); | |
2861 return stub.GetCode(); | |
2862 } | |
2863 | |
2864 | |
2865 Handle<Code> GetTypeRecordingBinaryOpStub(int key, | 1783 Handle<Code> GetTypeRecordingBinaryOpStub(int key, |
2866 TRBinaryOpIC::TypeInfo type_info, | 1784 TRBinaryOpIC::TypeInfo type_info, |
2867 TRBinaryOpIC::TypeInfo result_type_info) { | 1785 TRBinaryOpIC::TypeInfo result_type_info) { |
2868 TypeRecordingBinaryOpStub stub(key, type_info, result_type_info); | 1786 TypeRecordingBinaryOpStub stub(key, type_info, result_type_info); |
2869 return stub.GetCode(); | 1787 return stub.GetCode(); |
2870 } | 1788 } |
2871 | 1789 |
2872 | 1790 |
2873 void TypeRecordingBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) { | 1791 void TypeRecordingBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) { |
2874 Label get_result; | 1792 Label get_result; |
(...skipping 4057 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6932 __ str(pc, MemOperand(sp, 0)); | 5850 __ str(pc, MemOperand(sp, 0)); |
6933 __ Jump(target); // Call the C++ function. | 5851 __ Jump(target); // Call the C++ function. |
6934 } | 5852 } |
6935 | 5853 |
6936 | 5854 |
6937 #undef __ | 5855 #undef __ |
6938 | 5856 |
6939 } } // namespace v8::internal | 5857 } } // namespace v8::internal |
6940 | 5858 |
6941 #endif // V8_TARGET_ARCH_ARM | 5859 #endif // V8_TARGET_ARCH_ARM |
OLD | NEW |