+2008-09-26 Gavin Barraclough <barraclough@apple.com>
+
+ Reviewed by Maciej Stachowiak & Oliver Hunt.
+
+ Add support for reusing temporary JSNumberCells. This change is based on the observation
+ that if the result of certain operations is a JSNumberCell and is consumed by a subsequent
+ operation that would produce a JSNumberCell, we can reuse the object rather than allocating
+ a fresh one. E.g. given the expression ((a * b) * c), we can statically determine that
+ (a * b) will have a numeric result (or else it will have thrown an exception), so the result
+ will either be a JSNumberCell or a JSImmediate.
+
+ This patch changes three areas of JSC:
+ * The AST now tracks type information about the result of each node.
+ * This information is consumed in bytecode compilation, and certain bytecode operations
+ now carry the statically determined type information about their operands.
+ * CTI uses the information in a number of fashions:
+ * Where an operand to certain arithmetic operations is reusable, it will plant code
+ to try to perform the operation in JIT code & reuse the cell, where appropriate.
+ * Where it can be statically determined that an operand can only be numeric (typically
+ the result of another arithmetic operation) the code will not redundantly check that
+ the JSCell is a JSNumberCell.
+ * Where either of the operands to an add are non-numeric do not plant an optimized
+ arithmetic code path, just call straight out to the C function.
+
+ +6% Sunspider (10% progression on 3D, 16% progression on math, 60% progression on access-nbody),
+ +1% v8-tests (improvements in raytrace & crypto)
+
+ * VM/CTI.cpp: Add optimized code generation with reuse of temporary JSNumberCells.
+ * VM/CTI.h:
+ * kjs/JSNumberCell.h:
+ * masm/X86Assembler.h:
+
+ * VM/CodeBlock.cpp: Add type information to specific bytecodes.
+ * VM/CodeGenerator.cpp:
+ * VM/CodeGenerator.h:
+ * VM/Machine.cpp:
+
+ * kjs/nodes.cpp: Track static type information for nodes.
+ * kjs/nodes.h:
+ * kjs/ResultDescriptor.h: (Added)
+ * JavaScriptCore.xcodeproj/project.pbxproj:
+
2008-09-26 Yichao Yin <yichao.yin@torchmobile.com.cn>
Reviewed by George Staikos, Maciej Stachowiak.
869081410E640C89000D36ED /* X86Assembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 869081400E640C89000D36ED /* X86Assembler.h */; settings = {ATTRIBUTES = (Private, ); }; };
869083150E6518D7000D36ED /* WREC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 869083130E6518D7000D36ED /* WREC.cpp */; };
869083160E6518D7000D36ED /* WREC.h in Headers */ = {isa = PBXBuildFile; fileRef = 869083140E6518D7000D36ED /* WREC.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 869EBCB70E8C6D4A008722CC /* ResultType.h in Headers */ = {isa = PBXBuildFile; fileRef = 869EBCB60E8C6D4A008722CC /* ResultType.h */; settings = {ATTRIBUTES = (Private, ); }; };
905B02AE0E28640F006DF882 /* RefCountedLeakCounter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 905B02AD0E28640F006DF882 /* RefCountedLeakCounter.cpp */; };
90D3469C0E285280009492EE /* RefCountedLeakCounter.h in Headers */ = {isa = PBXBuildFile; fileRef = 90D3469B0E285280009492EE /* RefCountedLeakCounter.h */; settings = {ATTRIBUTES = (Private, ); }; };
930754C108B0F68000AB3056 /* pcre_compile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 930754BF08B0F68000AB3056 /* pcre_compile.cpp */; };
869081400E640C89000D36ED /* X86Assembler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = X86Assembler.h; sourceTree = "<group>"; };
869083130E6518D7000D36ED /* WREC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WREC.cpp; sourceTree = "<group>"; };
869083140E6518D7000D36ED /* WREC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WREC.h; sourceTree = "<group>"; };
+ 869EBCB60E8C6D4A008722CC /* ResultType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResultType.h; sourceTree = "<group>"; };
905B02AD0E28640F006DF882 /* RefCountedLeakCounter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RefCountedLeakCounter.cpp; sourceTree = "<group>"; };
90D3469B0E285280009492EE /* RefCountedLeakCounter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RefCountedLeakCounter.h; sourceTree = "<group>"; };
9303F567099118FA00AD71B8 /* OwnPtr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OwnPtr.h; sourceTree = "<group>"; };
F692A87C0255597D01FF60F7 /* RegExpObject.h */,
BCD202BF0E1706A7002C7E82 /* RegExpPrototype.cpp */,
BCD202C00E1706A7002C7E82 /* RegExpPrototype.h */,
+ 869EBCB60E8C6D4A008722CC /* ResultType.h */,
9374D3A8038D9D74008635CE /* ScopeChain.cpp */,
9374D3A7038D9D74008635CE /* ScopeChain.h */,
7E2C6C980D31C6B6002D44E2 /* ScopeChainMark.h */,
147B84630E6DE6B1004775A4 /* PutPropertySlot.h in Headers */,
7E2ADD8E0E79AAD500D50C51 /* CharacterClassConstructor.h in Headers */,
140D17D70E8AD4A9000CD17D /* JSBasePrivate.h in Headers */,
+ 869EBCB70E8C6D4A008722CC /* ResultType.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
isa = XCBuildConfiguration;
baseConfigurationReference = 1C9051440BA9E8A70081E9D0 /* DebugRelease.xcconfig */;
buildSettings = {
+ GCC_VERSION = 4.0;
STRIP_INSTALLED_PRODUCT = NO;
};
name = Release;
#include "JSArray.h"
#include "Machine.h"
#include "wrec/WREC.h"
+#include "ResultType.h"
+#if PLATFORM(MAC)
+#include <sys/sysctl.h>
+#endif
using namespace std;
namespace JSC {
+#if PLATFORM(MAC)
+bool isSSE3Present()
+{
+ struct SSE3Check {
+ SSE3Check()
+ {
+ int hasSSE3 = 0;
+ size_t length = sizeof(hasSSE3);
+ int error = sysctlbyname("hw.optional.sse3", &hasSSE3, &length, NULL, 0);
+ present = hasSSE3 && !error;
+ }
+ bool present;
+ };
+ static SSE3Check check;
+ return check.present;
+}
+#else
+bool isSSE3Present()
+{
+ return false;
+}
+#endif
+
#if COMPILER(GCC) && PLATFORM(X86)
asm(
".globl _ctiTrampoline" "\n"
m_jit.link(skipTimeout, m_jit.label());
}
+/*
+ This is required since number representation is canonical - values representable as a JSImmediate should not be stored in a JSNumberCell.
+
+ In the common case, the double value from 'xmmSource' is written to the reusable JSNumberCell pointed to by 'jsNumberCell', then 'jsNumberCell'
+ is written to the output SF Register 'dst', and then a jump is planted (stored into *wroteJSNumberCell).
+
+ However if the value from xmmSource is representable as a JSImmediate, then the JSImmediate value will be written to the output, and flow
+ control will fall through from the code planted.
+*/
+void CTI::putDoubleResultToJSNumberCellOrJSImmediate(X86::XMMRegisterID xmmSource, X86::RegisterID jsNumberCell, unsigned dst, X86Assembler::JmpSrc* wroteJSNumberCell, X86::XMMRegisterID tempXmm, X86::RegisterID tempReg1, X86::RegisterID tempReg2)
+{
+ // convert (double -> JSImmediate -> double), and check if the value is unchanged - in which case the value is representable as a JSImmediate.
+ m_jit.cvttsd2si_rr(xmmSource, tempReg1);
+ m_jit.addl_rr(tempReg1, tempReg1);
+ m_jit.sarl_i8r(1, tempReg1);
+ m_jit.cvtsi2sd_rr(tempReg1, tempXmm);
+ // Compare & branch if immediate.
+ m_jit.ucomis_rr(tempXmm, xmmSource);
+ X86Assembler::JmpSrc resultIsImm = m_jit.emitUnlinkedJe();
+ X86Assembler::JmpDst resultLookedLikeImmButActuallyIsnt = m_jit.label();
+
+ // Store the result to the JSNumberCell and jump.
+ m_jit.movsd_rm(xmmSource, OBJECT_OFFSET(JSNumberCell, m_value), jsNumberCell);
+ emitPutResult(dst, jsNumberCell);
+ *wroteJSNumberCell = m_jit.emitUnlinkedJmp();
+
+ m_jit.link(resultIsImm, m_jit.label());
+ // value == (double)(JSImmediate)value... or at least, it looks that way...
+ // ucomi will report that (0 == -0), and will report true if either input in NaN (result is unordered).
+ m_jit.link(m_jit.emitUnlinkedJp(), resultLookedLikeImmButActuallyIsnt); // Actually was a NaN
+ m_jit.pextrw_irr(3, xmmSource, tempReg2);
+ m_jit.cmpl_i32r(0x8000, tempReg2);
+ m_jit.link(m_jit.emitUnlinkedJe(), resultLookedLikeImmButActuallyIsnt); // Actually was -0
+ // Yes it really really really is representable as a JSImmediate.
+ emitFastArithIntToImmNoCheck(tempReg1);
+ emitPutResult(dst, X86::ecx);
+}
+
+void CTI::compileBinaryArithOp(OpcodeID opcodeID, unsigned dst, unsigned src1, unsigned src2, OperandTypes types, unsigned i)
+{
+ StructureID* numberStructureID = m_exec->globalData().numberStructureID.get();
+ X86Assembler::JmpSrc wasJSNumberCell1, wasJSNumberCell1b, wasJSNumberCell2, wasJSNumberCell2b;
+
+ emitGetArg(src1, X86::eax);
+ emitGetArg(src2, X86::edx);
+
+ if (types.second().isReusable() && isSSE3Present()) {
+ ASSERT(types.second().mightBeNumber());
+
+ // Check op2 is a number
+ m_jit.testl_i32r(JSImmediate::TagBitTypeInteger, X86::edx);
+ X86Assembler::JmpSrc op2imm = m_jit.emitUnlinkedJne();
+ if (!types.second().definitelyIsNumber()) {
+ emitJumpSlowCaseIfNotJSCell(X86::edx, i);
+ m_jit.cmpl_i32m(reinterpret_cast<unsigned>(numberStructureID), OBJECT_OFFSET(JSCell, m_structureID), X86::edx);
+ m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJne(), i));
+ }
+
+ // (1) In this case src2 is a reusable number cell.
+ // Slow case if src1 is not a number type.
+ m_jit.testl_i32r(JSImmediate::TagBitTypeInteger, X86::eax);
+ X86Assembler::JmpSrc op1imm = m_jit.emitUnlinkedJne();
+ if (!types.first().definitelyIsNumber()) {
+ emitJumpSlowCaseIfNotJSCell(X86::eax, i);
+ m_jit.cmpl_i32m(reinterpret_cast<unsigned>(numberStructureID), OBJECT_OFFSET(JSCell, m_structureID), X86::eax);
+ m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJne(), i));
+ }
+
+ // (1a) if we get here, src1 is also a number cell
+ m_jit.movsd_mr(OBJECT_OFFSET(JSNumberCell, m_value), X86::eax, X86::xmm0);
+ X86Assembler::JmpSrc loadedDouble = m_jit.emitUnlinkedJmp();
+ // (1b) if we get here, src1 is an immediate
+ m_jit.link(op1imm, m_jit.label());
+ emitFastArithImmToInt(X86::eax);
+ m_jit.cvtsi2sd_rr(X86::eax, X86::xmm0);
+ // (1c)
+ m_jit.link(loadedDouble, m_jit.label());
+ if (opcodeID == op_add)
+ m_jit.addsd_mr(OBJECT_OFFSET(JSNumberCell, m_value), X86::edx, X86::xmm0);
+ else if (opcodeID == op_sub)
+ m_jit.subsd_mr(OBJECT_OFFSET(JSNumberCell, m_value), X86::edx, X86::xmm0);
+ else {
+ ASSERT(opcodeID == op_mul);
+ m_jit.mulsd_mr(OBJECT_OFFSET(JSNumberCell, m_value), X86::edx, X86::xmm0);
+ }
+
+ putDoubleResultToJSNumberCellOrJSImmediate(X86::xmm0, X86::edx, dst, &wasJSNumberCell2, X86::xmm1, X86::ecx, X86::eax);
+ wasJSNumberCell2b = m_jit.emitUnlinkedJmp();
+
+ // (2) This handles cases where src2 is an immediate number.
+ // Two slow cases - either src1 isn't an immediate, or the subtract overflows.
+ m_jit.link(op2imm, m_jit.label());
+ emitJumpSlowCaseIfNotImmNum(X86::eax, i);
+ } else if (types.first().isReusable() && isSSE3Present()) {
+ ASSERT(types.first().mightBeNumber());
+
+ // Check op1 is a number
+ m_jit.testl_i32r(JSImmediate::TagBitTypeInteger, X86::eax);
+ X86Assembler::JmpSrc op1imm = m_jit.emitUnlinkedJne();
+ if (!types.first().definitelyIsNumber()) {
+ emitJumpSlowCaseIfNotJSCell(X86::eax, i);
+ m_jit.cmpl_i32m(reinterpret_cast<unsigned>(numberStructureID), OBJECT_OFFSET(JSCell, m_structureID), X86::eax);
+ m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJne(), i));
+ }
+
+ // (1) In this case src1 is a reusable number cell.
+ // Slow case if src2 is not a number type.
+ m_jit.testl_i32r(JSImmediate::TagBitTypeInteger, X86::edx);
+ X86Assembler::JmpSrc op2imm = m_jit.emitUnlinkedJne();
+ if (!types.second().definitelyIsNumber()) {
+ emitJumpSlowCaseIfNotJSCell(X86::edx, i);
+ m_jit.cmpl_i32m(reinterpret_cast<unsigned>(numberStructureID), OBJECT_OFFSET(JSCell, m_structureID), X86::edx);
+ m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJne(), i));
+ }
+
+ // (1a) if we get here, src2 is also a number cell
+ m_jit.movsd_mr(OBJECT_OFFSET(JSNumberCell, m_value), X86::edx, X86::xmm1);
+ X86Assembler::JmpSrc loadedDouble = m_jit.emitUnlinkedJmp();
+ // (1b) if we get here, src2 is an immediate
+ m_jit.link(op2imm, m_jit.label());
+ emitFastArithImmToInt(X86::edx);
+ m_jit.cvtsi2sd_rr(X86::edx, X86::xmm1);
+ // (1c)
+ m_jit.link(loadedDouble, m_jit.label());
+ m_jit.movsd_mr(OBJECT_OFFSET(JSNumberCell, m_value), X86::eax, X86::xmm0);
+ if (opcodeID == op_add)
+ m_jit.addsd_rr(X86::xmm1, X86::xmm0);
+ else if (opcodeID == op_sub)
+ m_jit.subsd_rr(X86::xmm1, X86::xmm0);
+ else {
+ ASSERT(opcodeID == op_mul);
+ m_jit.mulsd_rr(X86::xmm1, X86::xmm0);
+ }
+ m_jit.movsd_rm(X86::xmm0, OBJECT_OFFSET(JSNumberCell, m_value), X86::eax);
+ emitPutResult(dst);
+
+ putDoubleResultToJSNumberCellOrJSImmediate(X86::xmm0, X86::eax, dst, &wasJSNumberCell1, X86::xmm1, X86::ecx, X86::edx);
+ wasJSNumberCell1b = m_jit.emitUnlinkedJmp();
+
+ // (2) This handles cases where src1 is an immediate number.
+ // Two slow cases - either src2 isn't an immediate, or the subtract overflows.
+ m_jit.link(op1imm, m_jit.label());
+ emitJumpSlowCaseIfNotImmNum(X86::edx, i);
+ } else
+ emitJumpSlowCaseIfNotImmNums(X86::eax, X86::edx, i);
+
+ if (opcodeID == op_add) {
+ emitFastArithDeTagImmediate(X86::eax);
+ m_jit.addl_rr(X86::edx, X86::eax);
+ m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
+ } else if (opcodeID == op_sub) {
+ m_jit.subl_rr(X86::edx, X86::eax);
+ m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
+ emitFastArithReTagImmediate(X86::eax);
+ } else {
+ ASSERT(opcodeID == op_mul);
+ emitFastArithDeTagImmediate(X86::eax);
+ emitFastArithImmToInt(X86::edx);
+ m_jit.imull_rr(X86::edx, X86::eax);
+ m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
+ emitFastArithReTagImmediate(X86::eax);
+ }
+ emitPutResult(dst);
+
+ if (types.second().isReusable() && isSSE3Present()) {
+ m_jit.link(wasJSNumberCell2, m_jit.label());
+ m_jit.link(wasJSNumberCell2b, m_jit.label());
+ }
+ else if (types.first().isReusable() && isSSE3Present()) {
+ m_jit.link(wasJSNumberCell1, m_jit.label());
+ m_jit.link(wasJSNumberCell1b, m_jit.label());
+ }
+}
+
+void CTI::compileBinaryArithOpSlowCase(OpcodeID opcodeID, Vector<SlowCaseEntry>::iterator& iter, unsigned dst, unsigned src1, unsigned src2, OperandTypes types, unsigned i)
+{
+ X86Assembler::JmpDst here = m_jit.label();
+ m_jit.link(iter->from, here);
+ if (types.second().isReusable() && isSSE3Present()) {
+ if (!types.first().definitelyIsNumber()) {
+ m_jit.link((++iter)->from, here);
+ m_jit.link((++iter)->from, here);
+ }
+ if (!types.second().definitelyIsNumber()) {
+ m_jit.link((++iter)->from, here);
+ m_jit.link((++iter)->from, here);
+ }
+ m_jit.link((++iter)->from, here);
+ } else if (types.first().isReusable() && isSSE3Present()) {
+ if (!types.first().definitelyIsNumber()) {
+ m_jit.link((++iter)->from, here);
+ m_jit.link((++iter)->from, here);
+ }
+ if (!types.second().definitelyIsNumber()) {
+ m_jit.link((++iter)->from, here);
+ m_jit.link((++iter)->from, here);
+ }
+ m_jit.link((++iter)->from, here);
+ } else
+ m_jit.link((++iter)->from, here);
+
+ emitGetPutArg(src1, 0, X86::ecx);
+ emitGetPutArg(src2, 4, X86::ecx);
+ if (opcodeID == op_add)
+ emitCall(i, Machine::cti_op_add);
+ else if (opcodeID == op_sub)
+ emitCall(i, Machine::cti_op_sub);
+ else {
+ ASSERT(opcodeID == op_mul);
+ emitCall(i, Machine::cti_op_mul);
+ }
+ emitPutResult(dst);
+}
+
void CTI::privateCompileMainPass()
{
Instruction* instruction = m_codeBlock->instructions.begin();
unsigned dst = instruction[i + 1].u.operand;
unsigned src1 = instruction[i + 2].u.operand;
unsigned src2 = instruction[i + 3].u.operand;
- if (isConstant(src2)) {
- JSValue* value = getConstant(m_exec, src2);
- if (JSImmediate::isNumber(value)) {
- emitGetArg(src1, X86::eax);
- emitJumpSlowCaseIfNotImmNum(X86::eax, i);
- m_jit.addl_i32r(getDeTaggedConstantImmediate(value), X86::eax);
- m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
- emitPutResult(dst);
- i += 4;
- break;
- }
- } else if (!isConstant(src1)) {
- emitGetArg(src1, X86::eax);
+
+ if (JSValue* value = getConstantImmediateNumericArg(src1)) {
emitGetArg(src2, X86::edx);
- emitJumpSlowCaseIfNotImmNums(X86::eax, X86::edx, i);
- emitFastArithDeTagImmediate(X86::eax);
- m_jit.addl_rr(X86::edx, X86::eax);
+ emitJumpSlowCaseIfNotImmNum(X86::edx, i);
+ m_jit.addl_i32r(getDeTaggedConstantImmediate(value), X86::edx);
+ m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
+ emitPutResult(dst, X86::edx);
+ } else if (JSValue* value = getConstantImmediateNumericArg(src2)) {
+ emitGetArg(src1, X86::eax);
+ emitJumpSlowCaseIfNotImmNum(X86::eax, i);
+ m_jit.addl_i32r(getDeTaggedConstantImmediate(value), X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
emitPutResult(dst);
- i += 4;
- break;
+ } else {
+ OperandTypes types = OperandTypes::fromInt(instruction[i + 4].u.operand);
+ if (types.first().mightBeNumber() && types.second().mightBeNumber())
+ compileBinaryArithOp(op_add, instruction[i + 1].u.operand, instruction[i + 2].u.operand, instruction[i + 3].u.operand, OperandTypes::fromInt(instruction[i + 4].u.operand), i);
+ else {
+ emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx);
+ emitGetPutArg(instruction[i + 3].u.operand, 4, X86::ecx);
+ emitCall(i, Machine::cti_op_add);
+ emitPutResult(instruction[i + 1].u.operand);
+ }
}
- emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx);
- emitGetPutArg(instruction[i + 3].u.operand, 4, X86::ecx);
- emitCall(i, Machine::cti_op_add);
- emitPutResult(instruction[i + 1].u.operand);
- i += 4;
+
+ i += 5;
break;
}
case op_end: {
unsigned dst = instruction[i + 1].u.operand;
unsigned src1 = instruction[i + 2].u.operand;
unsigned src2 = instruction[i + 3].u.operand;
- if (isConstant(src1) || isConstant(src2)) {
- unsigned constant = src1;
- unsigned nonconstant = src2;
- if (!isConstant(src1)) {
- constant = src2;
- nonconstant = src1;
- }
- JSValue* value = getConstant(m_exec, constant);
- if (JSImmediate::isNumber(value)) {
- emitGetArg(nonconstant, X86::eax);
- emitJumpSlowCaseIfNotImmNum(X86::eax, i);
- emitFastArithImmToInt(X86::eax);
- m_jit.imull_i32r( X86::eax, getDeTaggedConstantImmediate(value), X86::eax);
- m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
- emitFastArithPotentiallyReTagImmediate(X86::eax);
- emitPutResult(dst);
- i += 4;
- break;
- }
- }
- emitGetArg(src1, X86::eax);
- emitGetArg(src2, X86::edx);
- emitJumpSlowCaseIfNotImmNums(X86::eax, X86::edx, i);
- emitFastArithDeTagImmediate(X86::eax);
- emitFastArithImmToInt(X86::edx);
- m_jit.imull_rr(X86::edx, X86::eax);
- m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
- emitFastArithPotentiallyReTagImmediate(X86::eax);
- emitPutResult(dst);
- i += 4;
+ if (JSValue* src1Value = getConstantImmediateNumericArg(src1)) {
+ emitGetArg(src2, X86::eax);
+ emitJumpSlowCaseIfNotImmNum(X86::eax, i);
+ emitFastArithImmToInt(X86::eax);
+ m_jit.imull_i32r(X86::eax, getDeTaggedConstantImmediate(src1Value), X86::eax);
+ m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
+ emitFastArithReTagImmediate(X86::eax);
+ emitPutResult(dst);
+ } else if (JSValue* src2Value = getConstantImmediateNumericArg(src2)) {
+ emitGetArg(src1, X86::eax);
+ emitJumpSlowCaseIfNotImmNum(X86::eax, i);
+ emitFastArithImmToInt(X86::eax);
+ m_jit.imull_i32r(X86::eax, getDeTaggedConstantImmediate(src2Value), X86::eax);
+ m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
+ emitFastArithReTagImmediate(X86::eax);
+ emitPutResult(dst);
+ } else
+ compileBinaryArithOp(op_mul, instruction[i + 1].u.operand, instruction[i + 2].u.operand, instruction[i + 3].u.operand, OperandTypes::fromInt(instruction[i + 4].u.operand), i);
+
+ i += 5;
break;
}
case op_new_func: {
break;
}
case op_sub: {
- emitGetArg(instruction[i + 2].u.operand, X86::eax);
- emitGetArg(instruction[i + 3].u.operand, X86::edx);
- emitJumpSlowCaseIfNotImmNums(X86::eax, X86::edx, i);
- m_jit.subl_rr(X86::edx, X86::eax);
- m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
- emitFastArithReTagImmediate(X86::eax);
- emitPutResult(instruction[i + 1].u.operand);
- i += 4;
+ compileBinaryArithOp(op_sub, instruction[i + 1].u.operand, instruction[i + 2].u.operand, instruction[i + 3].u.operand, OperandTypes::fromInt(instruction[i + 4].u.operand), i);
+ i += 5;
break;
}
case op_put_by_val: {
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
emitPutResult(dst);
}
- i += 4;
+ i += 5;
break;
}
case op_rshift: {
m_jit.xorl_rr(X86::edx, X86::eax);
emitFastArithReTagImmediate(X86::eax);
emitPutResult(instruction[i + 1].u.operand);
- i += 4;
+ i += 5;
break;
}
case op_new_regexp: {
emitJumpSlowCaseIfNotImmNums(X86::eax, X86::edx, i);
m_jit.orl_rr(X86::edx, X86::eax);
emitPutResult(instruction[i + 1].u.operand);
- i += 4;
+ i += 5;
break;
}
case op_call_eval: {
switch (m_machine->getOpcodeID(instruction[i].u.opcode)) {
case op_add: {
unsigned dst = instruction[i + 1].u.operand;
+ unsigned src1 = instruction[i + 2].u.operand;
unsigned src2 = instruction[i + 3].u.operand;
- if (isConstant(src2)) {
- JSValue* value = getConstant(m_exec, src2);
- if (JSImmediate::isNumber(value)) {
- X86Assembler::JmpSrc notImm = iter->from;
- m_jit.link((++iter)->from, m_jit.label());
- m_jit.subl_i32r(getDeTaggedConstantImmediate(value), X86::eax);
- m_jit.link(notImm, m_jit.label());
- emitPutArg(X86::eax, 0);
- emitGetPutArg(src2, 4, X86::ecx);
- emitCall(i, Machine::cti_op_add);
- emitPutResult(dst);
- i += 4;
- break;
- }
+ if (JSValue* value = getConstantImmediateNumericArg(src1)) {
+ X86Assembler::JmpSrc notImm = iter->from;
+ m_jit.link((++iter)->from, m_jit.label());
+ m_jit.subl_i32r(getDeTaggedConstantImmediate(value), X86::edx);
+ m_jit.link(notImm, m_jit.label());
+ emitGetPutArg(src1, 0, X86::ecx);
+ emitPutArg(X86::edx, 4);
+ emitCall(i, Machine::cti_op_add);
+ emitPutResult(dst);
+ } else if (JSValue* value = getConstantImmediateNumericArg(src2)) {
+ X86Assembler::JmpSrc notImm = iter->from;
+ m_jit.link((++iter)->from, m_jit.label());
+ m_jit.subl_i32r(getDeTaggedConstantImmediate(value), X86::eax);
+ m_jit.link(notImm, m_jit.label());
+ emitPutArg(X86::eax, 0);
+ emitGetPutArg(src2, 4, X86::ecx);
+ emitCall(i, Machine::cti_op_add);
+ emitPutResult(dst);
+ } else {
+ OperandTypes types = OperandTypes::fromInt(instruction[i + 4].u.operand);
+ if (types.first().mightBeNumber() && types.second().mightBeNumber())
+ compileBinaryArithOpSlowCase(op_add, iter, dst, src1, src2, types, i);
+ else
+ ASSERT_NOT_REACHED();
}
- ASSERT(!isConstant(instruction[i + 2].u.operand));
-
- X86Assembler::JmpSrc notImm = iter->from;
- m_jit.link((++iter)->from, m_jit.label());
- m_jit.subl_rr(X86::edx, X86::eax);
- emitFastArithReTagImmediate(X86::eax);
- m_jit.link(notImm, m_jit.label());
- emitPutArg(X86::eax, 0);
- emitPutArg(X86::edx, 4);
- emitCall(i, Machine::cti_op_add);
- emitPutResult(dst);
- i += 4;
+ i += 5;
break;
}
case op_get_by_val: {
break;
}
case op_sub: {
- X86Assembler::JmpSrc notImm = iter->from;
- m_jit.link((++iter)->from, m_jit.label());
- m_jit.addl_rr(X86::edx, X86::eax);
- m_jit.link(notImm, m_jit.label());
- emitPutArg(X86::eax, 0);
- emitPutArg(X86::edx, 4);
- emitCall(i, Machine::cti_op_sub);
- emitPutResult(instruction[i + 1].u.operand);
- i += 4;
+ compileBinaryArithOpSlowCase(op_sub, iter, instruction[i + 1].u.operand, instruction[i + 2].u.operand, instruction[i + 3].u.operand, OperandTypes::fromInt(instruction[i + 4].u.operand), i);
+ i += 5;
break;
}
case op_rshift: {
// As for the hot path of get_by_id, above, we ensure that we can use an architecture specific offset
// so that we only need track one pointer into the slow case code - we track a pointer to the location
// of the call (which we can use to look up the repatch information), but should a array-length or
- // prototype access tramopile fail we want to bail out back to here. To do so we can subtract back
+ // prototype access trampoline fail we want to bail out back to here. To do so we can subtract back
// the distance from the call to the head of the slow case.
m_jit.link(iter->from, m_jit.label());
emitCall(i, Machine::cti_op_bitand);
emitPutResult(dst);
}
- i += 4;
+ i += 5;
break;
}
case op_jtrue: {
emitPutArg(X86::edx, 4);
emitCall(i, Machine::cti_op_bitxor);
emitPutResult(instruction[i + 1].u.operand);
- i += 4;
+ i += 5;
break;
}
case op_bitor: {
emitPutArg(X86::edx, 4);
emitCall(i, Machine::cti_op_bitor);
emitPutResult(instruction[i + 1].u.operand);
- i += 4;
+ i += 5;
break;
}
case op_eq: {
i += 4;
break;
}
- CTI_COMPILE_BINARY_OP_SLOW_CASE(op_mul);
+ case op_mul: {
+ int dst = instruction[i + 1].u.operand;
+ int src1 = instruction[i + 2].u.operand;
+ int src2 = instruction[i + 3].u.operand;
+ if (getConstantImmediateNumericArg(src1) || getConstantImmediateNumericArg(src2)) {
+ m_jit.link(iter->from, m_jit.label());
+ emitGetPutArg(src1, 0, X86::ecx);
+ emitGetPutArg(src2, 4, X86::ecx);
+ emitCall(i, Machine::cti_op_mul);
+ emitPutResult(dst);
+ } else
+ compileBinaryArithOpSlowCase(op_mul, iter, dst, src1, src2, OperandTypes::fromInt(instruction[i + 4].u.operand), i);
+ i += 5;
+ break;
+ }
+
case op_call:
case op_call_eval:
case op_construct: {
class StringJumpTable;
class StructureIDChain;
struct Instruction;
+ struct OperandTypes;
typedef JSValue* (*CTIHelper_j)(CTI_ARGS);
typedef JSPropertyNameIterator* (*CTIHelper_p)(CTI_ARGS);
void compileOpCall(Instruction* instruction, unsigned i, CompileOpCallType type = OpCallNormal);
enum CompileOpStrictEqType { OpStrictEq, OpNStrictEq };
void compileOpStrictEq(Instruction* instruction, unsigned i, CompileOpStrictEqType type);
+ void putDoubleResultToJSNumberCellOrJSImmediate(X86::XMMRegisterID xmmSource, X86::RegisterID jsNumberCell, unsigned dst, X86Assembler::JmpSrc* wroteJSNumberCell, X86::XMMRegisterID tempXmm, X86::RegisterID tempReg1, X86::RegisterID tempReg2);
+ void compileBinaryArithOp(OpcodeID, unsigned dst, unsigned src1, unsigned src2, OperandTypes opi, unsigned i);
+ void compileBinaryArithOpSlowCase(OpcodeID, Vector<SlowCaseEntry>::iterator& iter, unsigned dst, unsigned src1, unsigned src2, OperandTypes opi, unsigned i);
void emitGetArg(unsigned src, X86Assembler::RegisterID dst);
void emitGetPutArg(unsigned src, unsigned offset, X86Assembler::RegisterID scratch);
}
case op_add: {
printBinaryOp(location, it, "add");
+ ++it;
break;
}
case op_mul: {
printBinaryOp(location, it, "mul");
+ ++it;
break;
}
case op_div: {
}
case op_sub: {
printBinaryOp(location, it, "sub");
+ ++it;
break;
}
case op_lshift: {
}
case op_bitand: {
printBinaryOp(location, it, "bitand");
+ ++it;
break;
}
case op_bitxor: {
printBinaryOp(location, it, "bitxor");
+ ++it;
break;
}
case op_bitor: {
printBinaryOp(location, it, "bitor");
+ ++it;
break;
}
case op_bitnot: {
return dst;
}
-RegisterID* CodeGenerator::emitBinaryOp(OpcodeID opcode, RegisterID* dst, RegisterID* src1, RegisterID* src2)
+RegisterID* CodeGenerator::emitBinaryOp(OpcodeID opcode, RegisterID* dst, RegisterID* src1, RegisterID* src2, OperandTypes types)
{
emitOpcode(opcode);
instructions().append(dst->index());
instructions().append(src1->index());
instructions().append(src2->index());
+
+ if (opcode == op_bitor || opcode == op_bitand || opcode == op_bitxor ||
+ opcode == op_add || opcode == op_mul || opcode == op_sub) {
+ instructions().append(types.toInt());
+ }
+
return dst;
}
RegisterID* emitUnexpectedLoad(RegisterID* dst, double);
RegisterID* emitUnaryOp(OpcodeID, RegisterID* dst, RegisterID* src);
- RegisterID* emitBinaryOp(OpcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2);
+ RegisterID* emitBinaryOp(OpcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2, OperandTypes);
RegisterID* emitEqualityOp(OpcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2);
RegisterID* emitUnaryNoDstOp(OpcodeID, RegisterID* src);
RegisterID* emitInstanceOf(RegisterID* dst, RegisterID* value, RegisterID* base, RegisterID* basePrototype);
RegisterID* emitTypeOf(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_typeof, dst, src); }
- RegisterID* emitIn(RegisterID* dst, RegisterID* property, RegisterID* base) { return emitBinaryOp(op_in, dst, property, base); }
+ RegisterID* emitIn(RegisterID* dst, RegisterID* property, RegisterID* base) { return emitBinaryOp(op_in, dst, property, base, OperandTypes()); }
RegisterID* emitResolve(RegisterID* dst, const Identifier& property);
RegisterID* emitGetScopedVar(RegisterID* dst, size_t skip, int index, JSValue* globalObject);
VM_CHECK_EXCEPTION();
r[dst] = result;
}
- ++vPC;
+ vPC += 2;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_mul) {
r[dst] = result;
}
- ++vPC;
+ vPC += 2;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_div) {
VM_CHECK_EXCEPTION();
r[dst] = result;
}
- ++vPC;
+ vPC += 2;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_lshift) {
r[dst] = result;
}
- ++vPC;
+ vPC += 2;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_bitxor) {
r[dst] = result;
}
- ++vPC;
+ vPC += 2;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_bitor) {
r[dst] = result;
}
- ++vPC;
+ vPC += 2;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_bitnot) {
struct Instruction;
class JSNumberCell : public JSCell {
+ friend class CTI;
friend JSValue* jsNumberCell(ExecState*, double);
friend JSValue* jsNaN(ExecState*);
public:
--- /dev/null
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ResultType_h
+#define ResultType_h
+
+namespace JSC {
+
+ struct ResultType {
+ friend struct OperandTypes;
+
+ typedef char Type;
+ static const Type TypeReusable = 1;
+
+ static const Type TypeMaybeNumber = 2;
+ static const Type TypeMaybeString = 4;
+ static const Type TypeMaybeNull = 8;
+ static const Type TypeMaybeBool = 16;
+ static const Type TypeMaybeOther = 32;
+
+ static const Type TypeReusableNumber = 3;
+ static const Type TypeStringOrReusableNumber = 4;
+
+ explicit ResultType(Type type)
+ : m_type(type)
+ {
+ }
+
+ bool isReusable()
+ {
+ return (m_type & TypeReusable);
+ }
+
+ bool definitelyIsNumber()
+ {
+ return ((m_type & ~TypeReusable) == TypeMaybeNumber);
+ }
+
+ bool isNotNumber()
+ {
+ return ((m_type & TypeMaybeNumber) == 0);
+ }
+
+ bool mightBeNumber()
+ {
+ return !isNotNumber();
+ }
+
+ static ResultType nullType()
+ {
+ return ResultType(TypeMaybeNull);
+ }
+
+ static ResultType boolean()
+ {
+ return ResultType(TypeMaybeBool);
+ }
+
+ static ResultType constNumber()
+ {
+ return ResultType(TypeMaybeNumber);
+ }
+
+ static ResultType reusableNumber()
+ {
+ return ResultType(TypeReusable | TypeMaybeNumber);
+ }
+
+ static ResultType reusableNumberOrString()
+ {
+ return ResultType(TypeReusable | TypeMaybeNumber | TypeMaybeString);
+ }
+
+ static ResultType string()
+ {
+ return ResultType(TypeMaybeString);
+ }
+
+ static ResultType unknown()
+ {
+ return ResultType(TypeMaybeNumber | TypeMaybeString | TypeMaybeNull | TypeMaybeBool | TypeMaybeOther);
+ }
+
+ static ResultType forAdd(ResultType op1, ResultType op2)
+ {
+ if (op1.definitelyIsNumber() && op2.definitelyIsNumber())
+ return reusableNumber();
+ if (op1.isNotNumber() || op2.isNotNumber())
+ return string();
+ return reusableNumberOrString();
+ }
+
+ private:
+ Type m_type;
+ };
+
+ struct OperandTypes
+ {
+ OperandTypes(ResultType first = ResultType::unknown(), ResultType second = ResultType::unknown())
+ {
+ m_u.rds.first = first.m_type;
+ m_u.rds.second = second.m_type;
+ }
+
+ union {
+ struct {
+ ResultType::Type first;
+ ResultType::Type second;
+ } rds;
+ int i;
+ } m_u;
+
+ ResultType first()
+ {
+ return ResultType(m_u.rds.first);
+ }
+
+ ResultType second()
+ {
+ return ResultType(m_u.rds.second);
+ }
+
+ int toInt()
+ {
+ return m_u.i;
+ }
+ static OperandTypes fromInt(int value)
+ {
+ OperandTypes types;
+ types.m_u.i = value;
+ return types;
+ }
+ };
+
+} // namespace JSC
+
+#endif // ResultType_h
Node::Node(JSGlobalData* globalData)
: ParserRefCounted(globalData)
- , m_expectedReturnType(ObjectType)
-{
- m_line = globalData->lexer->lineNo();
-}
-
-Node::Node(JSGlobalData* globalData, JSType expectedReturn)
- : ParserRefCounted(globalData)
- , m_expectedReturnType(expectedReturn)
{
m_line = globalData->lexer->lineNo();
}
if (dst == ignoredResult())
return 0;
RefPtr<RegisterID> r0 = generator.emitUnexpectedLoad(generator.finalDestination(dst), (m_operator == OpPlusPlus) ? 1.0 : -1.0);
- return generator.emitBinaryOp(op_add, r0.get(), local, r0.get());
+ return generator.emitBinaryOp(op_add, r0.get(), local, r0.get(), OperandTypes());
}
emitPreIncOrDec(generator, local, m_operator);
RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1.get(), m_rightHasAssignments, m_expr2->isPure(generator));
RegisterID* src2 = generator.emitNode(m_expr2.get());
- return generator.emitBinaryOp(opcode, generator.finalDestination(dst, src1.get()), src1.get(), src2);
+ return generator.emitBinaryOp(opcode, generator.finalDestination(dst, src1.get()), src1.get(), src2, OperandTypes(m_expr1->resultDescriptor(), m_expr2->resultDescriptor()));
}
RegisterID* EqualNode::emitCode(CodeGenerator& generator, RegisterID* dst)
{
RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1.get(), m_rightHasAssignments, m_expr2->isPure(generator));
RegisterID* src2 = generator.emitNode(m_expr2.get());
- return generator.emitBinaryOp(opcode(), generator.finalDestination(dst, src1.get()), src2, src1.get());
+ return generator.emitBinaryOp(opcode(), generator.finalDestination(dst, src1.get()), src2, src1.get(), OperandTypes(m_expr2->resultDescriptor(), m_expr1->resultDescriptor()));
}
RegisterID* ThrowableBinaryOpNode::emitCode(CodeGenerator& generator, RegisterID* dst)
RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1.get(), m_rightHasAssignments, m_expr2->isPure(generator));
RegisterID* src2 = generator.emitNode(m_expr2.get());
generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset);
- return generator.emitBinaryOp(opcode(), generator.finalDestination(dst, src1.get()), src1.get(), src2);
+ return generator.emitBinaryOp(opcode(), generator.finalDestination(dst, src1.get()), src1.get(), src2, OperandTypes(m_expr1->resultDescriptor(), m_expr2->resultDescriptor()));
}
RegisterID* InstanceOfNode::emitCode(CodeGenerator& generator, RegisterID* dst)
// ------------------------------ ReadModifyResolveNode -----------------------------------
// FIXME: should this be moved to be a method on CodeGenerator?
-static ALWAYS_INLINE RegisterID* emitReadModifyAssignment(CodeGenerator& generator, RegisterID* dst, RegisterID* src1, RegisterID* src2, Operator oper)
+static ALWAYS_INLINE RegisterID* emitReadModifyAssignment(CodeGenerator& generator, RegisterID* dst, RegisterID* src1, RegisterID* src2, Operator oper, OperandTypes types)
{
OpcodeID opcode;
switch (oper) {
return dst;
}
- return generator.emitBinaryOp(opcode, dst, src1, src2);
+ return generator.emitBinaryOp(opcode, dst, src1, src2, types);
}
RegisterID* ReadModifyResolveNode::emitCode(CodeGenerator& generator, RegisterID* dst)
if (RegisterID* local = generator.registerFor(m_ident)) {
if (generator.isLocalConstant(m_ident)) {
RegisterID* src2 = generator.emitNode(m_right.get());
- return emitReadModifyAssignment(generator, generator.finalDestination(dst), local, src2, m_operator);
+ return emitReadModifyAssignment(generator, generator.finalDestination(dst), local, src2, m_operator, OperandTypes(ResultType::unknown(), m_right->resultDescriptor()));
}
if (generator.leftHandSideNeedsCopy(m_rightHasAssignments, m_right->isPure(generator))) {
RefPtr<RegisterID> result = generator.newTemporary();
generator.emitMove(result.get(), local);
RegisterID* src2 = generator.emitNode(m_right.get());
- emitReadModifyAssignment(generator, result.get(), result.get(), src2, m_operator);
+ emitReadModifyAssignment(generator, result.get(), result.get(), src2, m_operator, OperandTypes(ResultType::unknown(), m_right->resultDescriptor()));
generator.emitMove(local, result.get());
return generator.moveToDestinationIfNeeded(dst, result.get());
}
RegisterID* src2 = generator.emitNode(m_right.get());
- RegisterID* result = emitReadModifyAssignment(generator, local, local, src2, m_operator);
+ RegisterID* result = emitReadModifyAssignment(generator, local, local, src2, m_operator, OperandTypes(ResultType::unknown(), m_right->resultDescriptor()));
return generator.moveToDestinationIfNeeded(dst, result);
}
if (generator.findScopedProperty(m_ident, index, depth, true, globalObject) && index != missingSymbolMarker()) {
RefPtr<RegisterID> src1 = generator.emitGetScopedVar(generator.tempDestination(dst), depth, index, globalObject);
RegisterID* src2 = generator.emitNode(m_right.get());
- RegisterID* result = emitReadModifyAssignment(generator, generator.finalDestination(dst, src1.get()), src1.get(), src2, m_operator);
+ RegisterID* result = emitReadModifyAssignment(generator, generator.finalDestination(dst, src1.get()), src1.get(), src2, m_operator, OperandTypes(ResultType::unknown(), m_right->resultDescriptor()));
generator.emitPutScopedVar(depth, index, result, globalObject);
return result;
}
RefPtr<RegisterID> base = generator.emitResolveWithBase(generator.newTemporary(), src1.get(), m_ident);
RegisterID* src2 = generator.emitNode(m_right.get());
generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset);
- RegisterID* result = emitReadModifyAssignment(generator, generator.finalDestination(dst, src1.get()), src1.get(), src2, m_operator);
+ RegisterID* result = emitReadModifyAssignment(generator, generator.finalDestination(dst, src1.get()), src1.get(), src2, m_operator, OperandTypes(ResultType::unknown(), m_right->resultDescriptor()));
return generator.emitPutById(base.get(), m_ident, result);
}
generator.emitExpressionInfo(m_divot - m_subexpressionDivotOffset, m_startOffset - m_subexpressionDivotOffset, m_subexpressionEndOffset);
RefPtr<RegisterID> value = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident);
RegisterID* change = generator.emitNode(m_right.get());
- RegisterID* updatedValue = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), change, m_operator);
+ RegisterID* updatedValue = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), change, m_operator, OperandTypes(ResultType::unknown(), m_right->resultDescriptor()));
generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset);
return generator.emitPutById(base.get(), m_ident, updatedValue);
generator.emitExpressionInfo(m_divot - m_subexpressionDivotOffset, m_startOffset - m_subexpressionDivotOffset, m_subexpressionEndOffset);
RefPtr<RegisterID> value = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property.get());
RegisterID* change = generator.emitNode(m_right.get());
- RegisterID* updatedValue = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), change, m_operator);
+ RegisterID* updatedValue = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), change, m_operator, OperandTypes(ResultType::unknown(), m_right->resultDescriptor()));
generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset);
generator.emitPutByVal(base.get(), property.get(), updatedValue);
for (ClauseListNode* list = m_list1.get(); list; list = list->getNext()) {
RefPtr<RegisterID> clauseVal = generator.newTemporary();
generator.emitNode(clauseVal.get(), list->getClause()->expr());
- generator.emitBinaryOp(op_stricteq, clauseVal.get(), clauseVal.get(), switchExpression);
+ generator.emitBinaryOp(op_stricteq, clauseVal.get(), clauseVal.get(), switchExpression, OperandTypes());
labelVector.append(generator.newLabel());
generator.emitJumpIfTrue(clauseVal.get(), labelVector[labelVector.size() - 1].get());
}
for (ClauseListNode* list = m_list2.get(); list; list = list->getNext()) {
RefPtr<RegisterID> clauseVal = generator.newTemporary();
generator.emitNode(clauseVal.get(), list->getClause()->expr());
- generator.emitBinaryOp(op_stricteq, clauseVal.get(), clauseVal.get(), switchExpression);
+ generator.emitBinaryOp(op_stricteq, clauseVal.get(), clauseVal.get(), switchExpression, OperandTypes());
labelVector.append(generator.newLabel());
generator.emitJumpIfTrue(clauseVal.get(), labelVector[labelVector.size() - 1].get());
}
#include "LabelStack.h"
#include "Opcode.h"
#include "RegisterID.h"
+#include "ResultType.h"
#include "SourceRange.h"
#include "SymbolTable.h"
#include "regexp.h"
virtual bool needsParensIfLeftmost() const { return false; }
protected:
- Node(JSGlobalData*, JSType) JSC_FAST_CALL; // used by ExpressionNode
-
- int m_line : 28;
- unsigned m_expectedReturnType : 3; // JSType
+ int m_line;
};
class ExpressionNode : public Node {
public:
- ExpressionNode(JSGlobalData* globalData) JSC_FAST_CALL : Node(globalData) {}
- ExpressionNode(JSGlobalData* globalData, JSType expectedReturn) JSC_FAST_CALL
- : Node(globalData, expectedReturn)
+ ExpressionNode(JSGlobalData* globalData, ResultType resultDesc = ResultType::unknown()) JSC_FAST_CALL
+ : Node(globalData)
+ , m_resultDesc(resultDesc)
{
}
virtual bool isBracketAccessorNode() const JSC_FAST_CALL { return false; }
virtual bool isDotAccessorNode() const JSC_FAST_CALL { return false; }
- JSType expectedReturnType() const JSC_FAST_CALL { return static_cast<JSType>(m_expectedReturnType); }
+ ResultType resultDescriptor() const JSC_FAST_CALL { return m_resultDesc; }
// This needs to be in public in order to compile using GCC 3.x
typedef enum { EvalOperator, FunctionCall } CallerType;
+
+ private:
+ ResultType m_resultDesc;
};
class StatementNode : public Node {
class NullNode : public ExpressionNode {
public:
NullNode(JSGlobalData* globalData) JSC_FAST_CALL
- : ExpressionNode(globalData, NullType)
+ : ExpressionNode(globalData, ResultType::nullType())
{
}
class BooleanNode : public ExpressionNode {
public:
BooleanNode(JSGlobalData* globalData, bool value) JSC_FAST_CALL
- : ExpressionNode(globalData, BooleanType)
+ : ExpressionNode(globalData, ResultType::boolean())
, m_value(value)
{
}
class NumberNode : public ExpressionNode {
public:
NumberNode(JSGlobalData* globalData, double v) JSC_FAST_CALL
- : ExpressionNode(globalData, NumberType)
+ : ExpressionNode(globalData, ResultType::constNumber())
, m_double(v)
{
}
class StringNode : public ExpressionNode {
public:
StringNode(JSGlobalData* globalData, const Identifier& v) JSC_FAST_CALL
- : ExpressionNode(globalData, StringType)
+ : ExpressionNode(globalData, ResultType::string())
, m_value(v)
{
}
class PrePostResolveNode : public ExpressionNode, public ThrowableExpressionData {
public:
PrePostResolveNode(JSGlobalData* globalData, const Identifier& ident, unsigned divot, unsigned startOffset, unsigned endOffset) JSC_FAST_CALL
- : ExpressionNode(globalData, NumberType)
+ : ExpressionNode(globalData, ResultType::constNumber()) // could be reusable for pre?
, ThrowableExpressionData(divot, startOffset, endOffset)
, m_ident(ident)
{
class TypeOfResolveNode : public ExpressionNode {
public:
TypeOfResolveNode(JSGlobalData* globalData, const Identifier& ident) JSC_FAST_CALL
- : ExpressionNode(globalData, StringType)
+ : ExpressionNode(globalData, ResultType::string())
, m_ident(ident)
{
}
class TypeOfValueNode : public ExpressionNode {
public:
TypeOfValueNode(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL
- : ExpressionNode(globalData, StringType)
+ : ExpressionNode(globalData, ResultType::string())
, m_expr(expr)
{
}
{
}
- UnaryOpNode(JSGlobalData* globalData, JSType type, ExpressionNode* expr)
+ UnaryOpNode(JSGlobalData* globalData, ResultType type, ExpressionNode* expr)
: ExpressionNode(globalData, type)
, m_expr(expr)
{
class UnaryPlusNode : public UnaryOpNode {
public:
UnaryPlusNode(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL
- : UnaryOpNode(globalData, NumberType, expr)
+ : UnaryOpNode(globalData, ResultType::constNumber(), expr)
{
}
class NegateNode : public UnaryOpNode {
public:
NegateNode(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL
- : UnaryOpNode(globalData, NumberType, expr)
+ : UnaryOpNode(globalData, ResultType::reusableNumber(), expr)
{
}
class BitwiseNotNode : public UnaryOpNode {
public:
BitwiseNotNode(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL
- : UnaryOpNode(globalData, NumberType, expr)
+ : UnaryOpNode(globalData, ResultType::reusableNumber(), expr)
{
}
class LogicalNotNode : public UnaryOpNode {
public:
LogicalNotNode(JSGlobalData* globalData, ExpressionNode* expr) JSC_FAST_CALL
- : UnaryOpNode(globalData, BooleanType, expr)
+ : UnaryOpNode(globalData, ResultType::boolean(), expr)
{
}
{
}
- BinaryOpNode(JSGlobalData* globalData, JSType type, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
+ BinaryOpNode(JSGlobalData* globalData, ResultType type, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
: ExpressionNode(globalData, type)
, m_expr1(expr1)
, m_expr2(expr2)
{
}
- ReverseBinaryOpNode(JSGlobalData* globalData, JSType type, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
+ ReverseBinaryOpNode(JSGlobalData* globalData, ResultType type, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
: ExpressionNode(globalData, type)
, m_expr1(expr1)
, m_expr2(expr2)
class MultNode : public BinaryOpNode {
public:
MultNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : BinaryOpNode(globalData, NumberType, expr1, expr2, rightHasAssignments)
+ : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments)
{
}
class DivNode : public BinaryOpNode {
public:
DivNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : BinaryOpNode(globalData, NumberType, expr1, expr2, rightHasAssignments)
+ : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments)
{
}
class ModNode : public BinaryOpNode {
public:
ModNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : BinaryOpNode(globalData, NumberType, expr1, expr2, rightHasAssignments)
+ : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments)
{
}
class AddNode : public BinaryOpNode {
public:
AddNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : BinaryOpNode(globalData, expr1, expr2, rightHasAssignments)
+ : BinaryOpNode(globalData, ResultType::forAdd(expr1->resultDescriptor(), expr2->resultDescriptor()), expr1, expr2, rightHasAssignments)
{
}
class SubNode : public BinaryOpNode {
public:
SubNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : BinaryOpNode(globalData, expr1, expr2, rightHasAssignments)
+ : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments)
{
}
class LeftShiftNode : public BinaryOpNode {
public:
LeftShiftNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : BinaryOpNode(globalData, NumberType, expr1, expr2, rightHasAssignments)
+ : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments)
{
}
class RightShiftNode : public BinaryOpNode {
public:
RightShiftNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : BinaryOpNode(globalData, NumberType, expr1, expr2, rightHasAssignments)
+ : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments)
{
}
class UnsignedRightShiftNode : public BinaryOpNode {
public:
UnsignedRightShiftNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : BinaryOpNode(globalData, NumberType, expr1, expr2, rightHasAssignments)
+ : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments)
{
}
class LessNode : public BinaryOpNode {
public:
LessNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : BinaryOpNode(globalData, BooleanType, expr1, expr2, rightHasAssignments)
+ : BinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments)
{
}
class GreaterNode : public ReverseBinaryOpNode {
public:
GreaterNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : ReverseBinaryOpNode(globalData, BooleanType, expr1, expr2, rightHasAssignments)
+ : ReverseBinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments)
{
}
class LessEqNode : public BinaryOpNode {
public:
LessEqNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : BinaryOpNode(globalData, BooleanType, expr1, expr2, rightHasAssignments)
+ : BinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments)
{
}
class GreaterEqNode : public ReverseBinaryOpNode {
public:
GreaterEqNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : ReverseBinaryOpNode(globalData, BooleanType, expr1, expr2, rightHasAssignments)
+ : ReverseBinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments)
{
}
class ThrowableBinaryOpNode : public BinaryOpNode, public ThrowableExpressionData {
public:
- ThrowableBinaryOpNode(JSGlobalData* globalData, JSType type, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
+ ThrowableBinaryOpNode(JSGlobalData* globalData, ResultType type, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
: BinaryOpNode(globalData, type, expr1, expr2, rightHasAssignments)
{
}
class InstanceOfNode : public ThrowableBinaryOpNode {
public:
InstanceOfNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : ThrowableBinaryOpNode(globalData, BooleanType, expr1, expr2, rightHasAssignments)
+ : ThrowableBinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments)
{
}
class EqualNode : public BinaryOpNode {
public:
EqualNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : BinaryOpNode(globalData, BooleanType, expr1, expr2, rightHasAssignments)
+ : BinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments)
{
}
class NotEqualNode : public BinaryOpNode {
public:
NotEqualNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : BinaryOpNode(globalData, BooleanType, expr1, expr2, rightHasAssignments)
+ : BinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments)
{
}
class StrictEqualNode : public BinaryOpNode {
public:
StrictEqualNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : BinaryOpNode(globalData, BooleanType, expr1, expr2, rightHasAssignments)
+ : BinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments)
{
}
class NotStrictEqualNode : public BinaryOpNode {
public:
NotStrictEqualNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : BinaryOpNode(globalData, BooleanType, expr1, expr2, rightHasAssignments)
+ : BinaryOpNode(globalData, ResultType::boolean(), expr1, expr2, rightHasAssignments)
{
}
class BitAndNode : public BinaryOpNode {
public:
BitAndNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : BinaryOpNode(globalData, NumberType, expr1, expr2, rightHasAssignments)
+ : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments)
{
}
class BitOrNode : public BinaryOpNode {
public:
BitOrNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : BinaryOpNode(globalData, NumberType, expr1, expr2, rightHasAssignments)
+ : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments)
{
}
class BitXOrNode : public BinaryOpNode {
public:
BitXOrNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) JSC_FAST_CALL
- : BinaryOpNode(globalData, NumberType, expr1, expr2, rightHasAssignments)
+ : BinaryOpNode(globalData, ResultType::reusableNumber(), expr1, expr2, rightHasAssignments)
{
}
class LogicalOpNode : public ExpressionNode {
public:
LogicalOpNode(JSGlobalData* globalData, ExpressionNode* expr1, ExpressionNode* expr2, LogicalOperator oper) JSC_FAST_CALL
- : ExpressionNode(globalData, BooleanType)
+ : ExpressionNode(globalData, ResultType::boolean())
, m_expr1(expr1)
, m_expr2(expr2)
, m_operator(oper)
hasSib = esp,
noScale = esp,
} RegisterID;
+
+ typedef enum {
+ xmm0,
+ xmm1,
+ xmm2,
+ xmm3,
+ xmm4,
+ xmm5,
+ xmm6,
+ xmm7,
+ } XMMRegisterID;
}
class X86Assembler {
public:
typedef X86::RegisterID RegisterID;
-
+ typedef X86::XMMRegisterID XMMRegisterID;
typedef enum {
OP_ADD_EvGv = 0x01,
OP_ADD_GvEv = 0x03,
OP_PUSH_EAX = 0x50,
OP_POP_EAX = 0x58,
PRE_OPERAND_SIZE = 0x66,
+ PRE_SSE_66 = 0x66,
OP_IMUL_GvEvIz = 0x69,
OP_GROUP1_EvIz = 0x81,
OP_GROUP1_EvIb = 0x83,
OP_GROUP2_EvCL = 0xD3,
OP_CALL_rel32 = 0xE8,
OP_JMP_rel32 = 0xE9,
+ PRE_SSE_F2 = 0xF2,
OP_GROUP3_Ev = 0xF7,
OP_GROUP3_EvIz = 0xF7, // OP_GROUP3_Ev has an immediate, when instruction is a test.
OP_GROUP5_Ev = 0xFF,
- OP2_JO_rel32 = 0x80,
- OP2_JB_rel32 = 0x82,
- OP2_JAE_rel32 = 0x83,
- OP2_JE_rel32 = 0x84,
- OP2_JNE_rel32 = 0x85,
- OP2_JBE_rel32 = 0x86,
- OP2_JA_rel32 = 0x87,
- OP2_JL_rel32 = 0x8C,
- OP2_JGE_rel32 = 0x8D,
- OP2_JLE_rel32 = 0x8E,
- OP2_IMUL_GvEv = 0xAF,
- OP2_MOVZX_GvEb = 0xB6,
- OP2_MOVZX_GvEw = 0xB7,
+ OP2_MOVSD_VsdWsd = 0x10,
+ OP2_MOVSD_WsdVsd = 0x11,
+ OP2_CVTSI2SD_VsdEd = 0x2A,
+ OP2_CVTTSD2SI_GdWsd = 0x2C,
+ OP2_UCOMISD_VsdWsd = 0x2E,
+ OP2_ADDSD_VsdWsd = 0x58,
+ OP2_MULSD_VsdWsd = 0x59,
+ OP2_SUBSD_VsdWsd = 0x5C,
+ OP2_MOVD_EdVd = 0x7E,
+ OP2_JO_rel32 = 0x80,
+ OP2_JB_rel32 = 0x82,
+ OP2_JAE_rel32 = 0x83,
+ OP2_JE_rel32 = 0x84,
+ OP2_JNE_rel32 = 0x85,
+ OP2_JBE_rel32 = 0x86,
+ OP2_JA_rel32 = 0x87,
+ OP2_JP_rel32 = 0x8A,
+ OP2_JL_rel32 = 0x8C,
+ OP2_JGE_rel32 = 0x8D,
+ OP2_JLE_rel32 = 0x8E,
+ OP2_IMUL_GvEv = 0xAF,
+ OP2_MOVZX_GvEb = 0xB6,
+ OP2_MOVZX_GvEw = 0xB7,
+ OP2_PEXTRW_GdUdIb = 0xC5,
GROUP1_OP_ADD = 0,
GROUP1_OP_OR = 1,
emitModRm_rm(dst, base, offset);
}
+ void leal_mr(int offset, RegisterID index, int scale, RegisterID dst)
+ {
+ m_buffer->putByte(OP_LEA);
+ emitModRm_rmsib(dst, X86::noBase, index, scale, offset);
+ }
+
void ret()
{
m_buffer->putByte(OP_RET);
emitModRm_opm(GROUP5_OP_JMPN, base, offset);
}
+ void movsd_mr(int offset, RegisterID base, XMMRegisterID dst)
+ {
+ m_buffer->putByte(PRE_SSE_F2);
+ m_buffer->putByte(OP_2BYTE_ESCAPE);
+ m_buffer->putByte(OP2_MOVSD_VsdWsd);
+ emitModRm_rm((RegisterID)dst, base, offset);
+ }
+
+ void movsd_rm(XMMRegisterID src, int offset, RegisterID base)
+ {
+ m_buffer->putByte(PRE_SSE_F2);
+ m_buffer->putByte(OP_2BYTE_ESCAPE);
+ m_buffer->putByte(OP2_MOVSD_WsdVsd);
+ emitModRm_rm((RegisterID)src, base, offset);
+ }
+
+ void movd_rr(XMMRegisterID src, RegisterID dst)
+ {
+ m_buffer->putByte(PRE_SSE_66);
+ m_buffer->putByte(OP_2BYTE_ESCAPE);
+ m_buffer->putByte(OP2_MOVD_EdVd);
+ emitModRm_rr((RegisterID)src, dst);
+ }
+
+ void cvtsi2sd_rr(RegisterID src, XMMRegisterID dst)
+ {
+ m_buffer->putByte(PRE_SSE_F2);
+ m_buffer->putByte(OP_2BYTE_ESCAPE);
+ m_buffer->putByte(OP2_CVTSI2SD_VsdEd);
+ emitModRm_rr((RegisterID)dst, src);
+ }
+
+ void cvttsd2si_rr(XMMRegisterID src, RegisterID dst)
+ {
+ m_buffer->putByte(PRE_SSE_F2);
+ m_buffer->putByte(OP_2BYTE_ESCAPE);
+ m_buffer->putByte(OP2_CVTTSD2SI_GdWsd);
+ emitModRm_rr(dst, (RegisterID)src);
+ }
+
+ void addsd_mr(int offset, RegisterID base, XMMRegisterID dst)
+ {
+ m_buffer->putByte(PRE_SSE_F2);
+ m_buffer->putByte(OP_2BYTE_ESCAPE);
+ m_buffer->putByte(OP2_ADDSD_VsdWsd);
+ emitModRm_rm((RegisterID)dst, base, offset);
+ }
+
+ void subsd_mr(int offset, RegisterID base, XMMRegisterID dst)
+ {
+ m_buffer->putByte(PRE_SSE_F2);
+ m_buffer->putByte(OP_2BYTE_ESCAPE);
+ m_buffer->putByte(OP2_SUBSD_VsdWsd);
+ emitModRm_rm((RegisterID)dst, base, offset);
+ }
+
+ void mulsd_mr(int offset, RegisterID base, XMMRegisterID dst)
+ {
+ m_buffer->putByte(PRE_SSE_F2);
+ m_buffer->putByte(OP_2BYTE_ESCAPE);
+ m_buffer->putByte(OP2_MULSD_VsdWsd);
+ emitModRm_rm((RegisterID)dst, base, offset);
+ }
+
+ void addsd_rr(XMMRegisterID src, XMMRegisterID dst)
+ {
+ m_buffer->putByte(PRE_SSE_F2);
+ m_buffer->putByte(OP_2BYTE_ESCAPE);
+ m_buffer->putByte(OP2_ADDSD_VsdWsd);
+ emitModRm_rr((RegisterID)dst, (RegisterID)src);
+ }
+
+ void subsd_rr(XMMRegisterID src, XMMRegisterID dst)
+ {
+ m_buffer->putByte(PRE_SSE_F2);
+ m_buffer->putByte(OP_2BYTE_ESCAPE);
+ m_buffer->putByte(OP2_SUBSD_VsdWsd);
+ emitModRm_rr((RegisterID)dst, (RegisterID)src);
+ }
+
+ void mulsd_rr(XMMRegisterID src, XMMRegisterID dst)
+ {
+ m_buffer->putByte(PRE_SSE_F2);
+ m_buffer->putByte(OP_2BYTE_ESCAPE);
+ m_buffer->putByte(OP2_MULSD_VsdWsd);
+ emitModRm_rr((RegisterID)dst, (RegisterID)src);
+ }
+
+ void ucomis_rr(XMMRegisterID src, XMMRegisterID dst)
+ {
+ m_buffer->putByte(PRE_SSE_66);
+ m_buffer->putByte(OP_2BYTE_ESCAPE);
+ m_buffer->putByte(OP2_UCOMISD_VsdWsd);
+ emitModRm_rr((RegisterID)dst, (RegisterID)src);
+ }
+
+ void pextrw_irr(int whichWord, XMMRegisterID src, RegisterID dst)
+ {
+ m_buffer->putByte(PRE_SSE_66);
+ m_buffer->putByte(OP_2BYTE_ESCAPE);
+ m_buffer->putByte(OP2_PEXTRW_GdUdIb);
+ emitModRm_rr(dst, (RegisterID)src);
+ m_buffer->putByte(whichWord);
+ }
+
// Opaque label types
class JmpSrc {
m_buffer->putInt(0);
return JmpSrc(m_buffer->getOffset());
}
+
+ JmpSrc emitUnlinkedJp()
+ {
+ m_buffer->putByte(OP_2BYTE_ESCAPE);
+ m_buffer->putByte(OP2_JP_rel32);
+ m_buffer->putInt(0);
+ return JmpSrc(m_buffer->getOffset());
+ }
void emitPredictionNotTaken()
{