Commit 3a4eb9b6 authored by barraclough@apple.com's avatar barraclough@apple.com

2008-10-17 Gavin Barraclough <barraclough@apple.com>

        Optimize op_call by allowing call sites to be directly linked to callees.

        For the hot path of op_call, CTI now generates a check (initially for an impossible
        value), and the first time the call is executed we attempt to link the call directly
        to the callee.  WWe can currently only do so if the arity of the caller and callee
        match.  The (optimized) setup for the call on the hot path is linked directly to
        the ctiCode for the callee, without indirection.
        
        Two forms of the slow case of the call are generated, the first will be executed the
        first time the call is reached.  As well as this path attempting to link the call to
        a callee, it also relinks the slow case to a second slow case, which will not continue
        to attempt relinking the call.  (This policy could be changed in future, but for not
        this is intended to prevent thrashing).

        If a callee that the caller has been linked to is garbage collected, then the link
        in the caller's JIt code will be reset back to a value that cannot match - to prevent
        any false positive matches.

        ~20% progression on deltablue & richards, >12% overall reduction in v8-tests
        runtime, one or two percent progression on sunspider.

        Reviewed by Oliver Hunt.

        * VM/CTI.cpp:
        (JSC::):
        (JSC::CTI::emitNakedCall):
        (JSC::unreachable):
        (JSC::CTI::compileOpCallInitializeCallFrame):
        (JSC::CTI::compileOpCallSetupArgs):
        (JSC::CTI::compileOpCall):
        (JSC::CTI::privateCompileMainPass):
        (JSC::CTI::privateCompileSlowCases):
        (JSC::CTI::privateCompile):
        (JSC::CTI::unlinkCall):
        (JSC::CTI::linkCall):
        * VM/CTI.h:
        * VM/CodeBlock.cpp:
        (JSC::CodeBlock::~CodeBlock):
        (JSC::CodeBlock::unlinkCallers):
        (JSC::CodeBlock::derefStructureIDs):
        * VM/CodeBlock.h:
        (JSC::StructureStubInfo::StructureStubInfo):
        (JSC::CallLinkInfo::CallLinkInfo):
        (JSC::CodeBlock::addCaller):
        (JSC::CodeBlock::removeCaller):
        (JSC::CodeBlock::getStubInfo):
        * VM/CodeGenerator.cpp:
        (JSC::CodeGenerator::emitCall):
        (JSC::CodeGenerator::emitConstruct):
        * VM/Machine.cpp:
        (JSC::Machine::cti_op_call_profiler):
        (JSC::Machine::cti_op_call_JSFunction):
        (JSC::Machine::cti_vm_lazyLinkCall):
        (JSC::Machine::cti_op_construct_JSConstructFast):
        (JSC::Machine::cti_op_construct_JSConstruct):
        (JSC::Machine::cti_op_construct_NotJSConstruct):
        * VM/Machine.h:
        * kjs/JSFunction.cpp:
        (JSC::JSFunction::~JSFunction):
        * kjs/JSFunction.h:
        * kjs/nodes.h:
        (JSC::FunctionBodyNode::):
        * masm/X86Assembler.h:
        (JSC::X86Assembler::getDifferenceBetweenLabels):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@37670 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 2d1d7a7f
2008-10-17 Gavin Barraclough <barraclough@apple.com>
Optimize op_call by allowing call sites to be directly linked to callees.
For the hot path of op_call, CTI now generates a check (initially for an impossible
value), and the first time the call is executed we attempt to link the call directly
to the callee. WWe can currently only do so if the arity of the caller and callee
match. The (optimized) setup for the call on the hot path is linked directly to
the ctiCode for the callee, without indirection.
Two forms of the slow case of the call are generated, the first will be executed the
first time the call is reached. As well as this path attempting to link the call to
a callee, it also relinks the slow case to a second slow case, which will not continue
to attempt relinking the call. (This policy could be changed in future, but for not
this is intended to prevent thrashing).
If a callee that the caller has been linked to is garbage collected, then the link
in the caller's JIt code will be reset back to a value that cannot match - to prevent
any false positive matches.
~20% progression on deltablue & richards, >12% overall reduction in v8-tests
runtime, one or two percent progression on sunspider.
Reviewed by Oliver Hunt.
* VM/CTI.cpp:
(JSC::):
(JSC::CTI::emitNakedCall):
(JSC::unreachable):
(JSC::CTI::compileOpCallInitializeCallFrame):
(JSC::CTI::compileOpCallSetupArgs):
(JSC::CTI::compileOpCall):
(JSC::CTI::privateCompileMainPass):
(JSC::CTI::privateCompileSlowCases):
(JSC::CTI::privateCompile):
(JSC::CTI::unlinkCall):
(JSC::CTI::linkCall):
* VM/CTI.h:
* VM/CodeBlock.cpp:
(JSC::CodeBlock::~CodeBlock):
(JSC::CodeBlock::unlinkCallers):
(JSC::CodeBlock::derefStructureIDs):
* VM/CodeBlock.h:
(JSC::StructureStubInfo::StructureStubInfo):
(JSC::CallLinkInfo::CallLinkInfo):
(JSC::CodeBlock::addCaller):
(JSC::CodeBlock::removeCaller):
(JSC::CodeBlock::getStubInfo):
* VM/CodeGenerator.cpp:
(JSC::CodeGenerator::emitCall):
(JSC::CodeGenerator::emitConstruct):
* VM/Machine.cpp:
(JSC::Machine::cti_op_call_profiler):
(JSC::Machine::cti_op_call_JSFunction):
(JSC::Machine::cti_vm_lazyLinkCall):
(JSC::Machine::cti_op_construct_JSConstructFast):
(JSC::Machine::cti_op_construct_JSConstruct):
(JSC::Machine::cti_op_construct_NotJSConstruct):
* VM/Machine.h:
* kjs/JSFunction.cpp:
(JSC::JSFunction::~JSFunction):
* kjs/JSFunction.h:
* kjs/nodes.h:
(JSC::FunctionBodyNode::):
* masm/X86Assembler.h:
(JSC::X86Assembler::getDifferenceBetweenLabels):
2008-10-17 Maciej Stachowiak <mjs@apple.com>
Reviewed by Geoff Garen.
......
This diff is collapsed.
......@@ -66,6 +66,7 @@
#define ARG_int3 ((int)((ARGS)[3]))
#define ARG_int4 ((int)((ARGS)[4]))
#define ARG_int5 ((int)((ARGS)[5]))
#define ARG_int6 ((int)((ARGS)[6]))
#define ARG_func1 ((FuncDeclNode*)((ARGS)[1]))
#define ARG_funcexp1 ((FuncExprNode*)((ARGS)[1]))
#define ARG_registers1 ((Register*)((ARGS)[1]))
......@@ -94,6 +95,7 @@ namespace JSC {
class StructureIDChain;
struct Instruction;
struct OperandTypes;
struct StructureStubInfo;
typedef JSValue* (SFX_CALL *CTIHelper_j)(CTI_ARGS);
typedef JSPropertyNameIterator* (SFX_CALL *CTIHelper_p)(CTI_ARGS);
......@@ -222,6 +224,8 @@ namespace JSC {
struct StructureStubCompilationInfo {
X86Assembler::JmpSrc callReturnLocation;
X86Assembler::JmpDst hotPathBegin;
X86Assembler::JmpSrc hotPathOther;
X86Assembler::JmpDst coldPathOther;
};
extern "C" {
......@@ -257,6 +261,7 @@ namespace JSC {
#else
static const int repatchOffsetGetByIdSlowCaseCall = 17 + 4 + ctiArgumentInitSize;
#endif
static const int repatchOffsetOpCallCall = 6;
public:
static void compile(Machine* machine, CallFrame* callFrame, CodeBlock* codeBlock)
......@@ -320,6 +325,9 @@ namespace JSC {
return cti.privateCompilePatchGetArrayLength(returnAddress);
}
static void linkCall(CodeBlock* callerCodeBlock, JSFunction* callee, CodeBlock* calleeCodeBlock, void* ctiCode, void* returnAddress, int callerArgCount);
static void unlinkCall(StructureStubInfo*);
inline static JSValue* execute(void* code, RegisterFile* registerFile, CallFrame* callFrame, JSGlobalData* globalData, JSValue** exception)
{
return ctiTrampoline(code, registerFile, callFrame, exception, Profiler::enabledProfilerReference(), globalData);
......@@ -346,8 +354,9 @@ namespace JSC {
void privateCompilePatchGetArrayLength(void* returnAddress);
enum CompileOpCallType { OpCallNormal, OpCallEval, OpConstruct };
void compileOpCall(Instruction* instruction, unsigned i, CompileOpCallType type = OpCallNormal);
void compileOpCall(Instruction* instruction, unsigned i, unsigned structureIDInstructionIndex, CompileOpCallType type = OpCallNormal);
void compileOpCallInitializeCallFrame(unsigned callee, unsigned argCount);
void compileOpCallSetupArgs(Instruction* instruction, bool isConstruct, bool isEval);
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);
......@@ -388,7 +397,8 @@ namespace JSC {
void emitTagAsBoolImmediate(X86Assembler::RegisterID reg);
X86Assembler::JmpSrc emitCall(unsigned opcodeIndex, X86::RegisterID);
X86Assembler::JmpSrc emitNakedCall(unsigned opcodeIndex, X86::RegisterID);
X86Assembler::JmpSrc emitNakedCall(unsigned opcodeIndex, void(*function)());
X86Assembler::JmpSrc emitCTICall(unsigned opcodeIndex, CTIHelper_j);
X86Assembler::JmpSrc emitCTICall(unsigned opcodeIndex, CTIHelper_p);
X86Assembler::JmpSrc emitCTICall(unsigned opcodeIndex, CTIHelper_v);
......
......@@ -30,6 +30,7 @@
#include "config.h"
#include "CodeBlock.h"
#include "CTI.h"
#include "JSValue.h"
#include "Machine.h"
#include "debugger.h"
......@@ -931,13 +932,34 @@ CodeBlock::~CodeBlock()
derefStructureIDs(&instructions[structureIDInstructions[i].opcodeIndex]);
if (structureIDInstructions[i].stubRoutine)
fastFree(structureIDInstructions[i].stubRoutine);
if (CallLinkInfo* callLinkInfo = structureIDInstructions[i].linkInfoPtr) {
callLinkInfo->callee->removeCaller(callLinkInfo);
delete callLinkInfo;
}
}
#if ENABLE(CTI)
#if ENABLE(CTI)
unlinkCallers();
if (ctiCode)
fastFree(ctiCode);
#endif
}
#if ENABLE(CTI)
void CodeBlock::unlinkCallers()
{
size_t size = linkedCallerList.size();
for (size_t i = 0; i < size; ++i) {
CallLinkInfo* currentCaller = linkedCallerList[i];
CTI::unlinkCall(currentCaller->callerStructureStubInfo);
currentCaller->callerStructureStubInfo->linkInfoPtr = 0;
delete currentCaller;
}
linkedCallerList.clear();
}
#endif
void CodeBlock::derefStructureIDs(Instruction* vPC) const
{
Machine* machine = globalData->machine;
......@@ -973,7 +995,8 @@ void CodeBlock::derefStructureIDs(Instruction* vPC) const
}
// These instructions don't ref their StructureIDs.
ASSERT(vPC[0].u.opcode == machine->getOpcode(op_get_by_id) || vPC[0].u.opcode == machine->getOpcode(op_put_by_id) || vPC[0].u.opcode == machine->getOpcode(op_get_by_id_generic) || vPC[0].u.opcode == machine->getOpcode(op_put_by_id_generic) || vPC[0].u.opcode == machine->getOpcode(op_get_array_length) || vPC[0].u.opcode == machine->getOpcode(op_get_string_length));
ASSERT(vPC[0].u.opcode == machine->getOpcode(op_get_by_id) || vPC[0].u.opcode == machine->getOpcode(op_put_by_id) || vPC[0].u.opcode == machine->getOpcode(op_get_by_id_generic) || vPC[0].u.opcode == machine->getOpcode(op_put_by_id_generic) || vPC[0].u.opcode == machine->getOpcode(op_get_array_length) || vPC[0].u.opcode == machine->getOpcode(op_get_string_length)
|| vPC[0].u.opcode == machine->getOpcode(op_call_eval) || vPC[0].u.opcode == machine->getOpcode(op_call) || vPC[0].u.opcode == machine->getOpcode(op_construct));
}
void CodeBlock::refStructureIDs(Instruction* vPC) const
......
......@@ -78,12 +78,17 @@ namespace JSC {
#endif
};
struct CallLinkInfo;
struct StructureStubInfo {
StructureStubInfo(unsigned opcodeIndex)
: opcodeIndex(opcodeIndex)
, stubRoutine(0)
, callReturnLocation(0)
, hotPathBegin(0)
, hotPathOther(0)
, coldPathOther(0)
, linkInfoPtr(0)
{
}
......@@ -91,6 +96,21 @@ namespace JSC {
void* stubRoutine;
void* callReturnLocation;
void* hotPathBegin;
void* hotPathOther;
void* coldPathOther;
CallLinkInfo* linkInfoPtr;
};
struct CallLinkInfo {
CodeBlock* callee;
StructureStubInfo* callerStructureStubInfo;
unsigned position;
CallLinkInfo(CodeBlock* c, StructureStubInfo* css)
{
callee = c;
callerStructureStubInfo = css;
}
};
struct StringJumpTable {
......@@ -203,6 +223,30 @@ namespace JSC {
~CodeBlock();
#if ENABLE(CTI)
void unlinkCallers();
#endif
void addCaller(StructureStubInfo* caller)
{
CallLinkInfo* callLinkInfo = new CallLinkInfo(this, caller);
caller->linkInfoPtr = callLinkInfo;
callLinkInfo->position = linkedCallerList.size();
linkedCallerList.append(callLinkInfo);
}
void removeCaller(CallLinkInfo* caller)
{
unsigned pos = caller->position;
unsigned lastPos = linkedCallerList.size() - 1;
if (pos != lastPos) {
linkedCallerList[pos] = linkedCallerList[lastPos];
linkedCallerList[pos]->position = pos;
}
linkedCallerList.shrink(lastPos);
}
#if !defined(NDEBUG) || ENABLE_SAMPLING_TOOL
void dump(ExecState*) const;
void printStructureIDs(const Instruction*) const;
......@@ -221,6 +265,7 @@ namespace JSC {
{
// FIXME: would a binary chop be faster here?
for (unsigned i = 0; ; ++i) {
ASSERT(i < structureIDInstructions.size());
if (structureIDInstructions[i].callReturnLocation == returnAddress)
return structureIDInstructions[i];
}
......@@ -251,6 +296,7 @@ namespace JSC {
Vector<Instruction> instructions;
Vector<StructureStubInfo> structureIDInstructions;
Vector<CallLinkInfo*> linkedCallerList;
// Constant pool
Vector<Identifier> identifiers;
......
......@@ -1153,6 +1153,7 @@ RegisterID* CodeGenerator::emitCall(OpcodeID opcodeID, RegisterID* dst, Register
callFrame.append(newTemporary());
emitExpressionInfo(divot, startOffset, endOffset);
m_codeBlock->structureIDInstructions.append(instructions().size());
emitOpcode(opcodeID);
instructions().append(dst->index());
instructions().append(func->index());
......@@ -1204,6 +1205,7 @@ RegisterID* CodeGenerator::emitConstruct(RegisterID* dst, RegisterID* func, Argu
callFrame.append(newTemporary());
emitExpressionInfo(divot, startOffset, endOffset);
m_codeBlock->structureIDInstructions.append(instructions().size());
emitOpcode(op_construct);
instructions().append(dst->index());
instructions().append(func->index());
......
......@@ -4544,6 +4544,12 @@ JSValue* Machine::cti_op_new_func(CTI_ARGS)
return ARG_func1->makeFunction(ARG_callFrame, ARG_callFrame->scopeChain());
}
void Machine::cti_op_call_profiler(CTI_ARGS)
{
ASSERT(*ARG_profilerReference);
(*ARG_profilerReference)->willExecute(ARG_callFrame, static_cast<JSFunction*>(ARG_src1));
}
VoidPtrPair Machine::cti_op_call_JSFunction(CTI_ARGS)
{
#ifndef NDEBUG
......@@ -4551,9 +4557,6 @@ VoidPtrPair Machine::cti_op_call_JSFunction(CTI_ARGS)
ASSERT(ARG_src1->getCallData(callData) == CallTypeJS);
#endif
if (UNLIKELY(*ARG_profilerReference != 0))
(*ARG_profilerReference)->willExecute(ARG_callFrame, static_cast<JSFunction*>(ARG_src1));
ScopeChainNode* callDataScopeChain = static_cast<JSFunction*>(ARG_src1)->m_scopeChain.node();
CodeBlock* newCodeBlock = &static_cast<JSFunction*>(ARG_src1)->m_body->byteCode(callDataScopeChain);
CallFrame* callFrame = ARG_callFrame;
......@@ -4593,6 +4596,24 @@ VoidPtrPair Machine::cti_op_call_JSFunction(CTI_ARGS)
return pair;
}
void* Machine::cti_vm_lazyLinkCall(CTI_ARGS)
{
Machine* machine = ARG_globalData->machine;
CallFrame* callFrame = CallFrame::create(ARG_callFrame);
CallFrame* callerCallFrame = callFrame->callerFrame();
CodeBlock* callerCodeBlock = callerCallFrame->codeBlock();
JSFunction* callee = static_cast<JSFunction*>(ARG_src1);
CodeBlock* codeBlock = &callee->m_body->byteCode(callee->m_scopeChain.node());
if (!codeBlock->ctiCode)
CTI::compile(machine, callFrame, codeBlock);
int argCount = ARG_int3;
CTI::linkCall(callerCodeBlock, callee, codeBlock, codeBlock->ctiCode, CTI_RETURN_ADDRESS, argCount);
return codeBlock->ctiCode;
}
void* Machine::cti_vm_compile(CTI_ARGS)
{
CodeBlock* codeBlock = ARG_callFrame->codeBlock();
......@@ -4715,24 +4736,36 @@ JSValue* Machine::cti_op_resolve(CTI_ARGS)
VM_THROW_EXCEPTION();
}
JSValue* Machine::cti_op_construct_JSConstructFast(CTI_ARGS)
{
#ifndef NDEBUG
ConstructData constructData;
ASSERT(static_cast<JSFunction*>(ARG_src1)->getConstructData(constructData) == ConstructTypeJS);
#endif
StructureID* structure;
if (ARG_src2->isObject())
structure = static_cast<JSObject*>(ARG_src2)->inheritorID();
else
structure = static_cast<JSFunction*>(ARG_src1)->m_scopeChain.node()->globalObject()->emptyObjectStructure();
return new (ARG_globalData) JSObject(structure);
}
VoidPtrPair Machine::cti_op_construct_JSConstruct(CTI_ARGS)
{
CallFrame* callFrame = ARG_callFrame;
JSFunction* constructor = static_cast<JSFunction*>(ARG_src1);
JSValue* constrProtoVal = ARG_src2;
int firstArg = ARG_int3;
int registerOffset = ARG_int4;
int argCount = ARG_int5;
int registerOffset = ARG_int2;
int argCount = ARG_int3;
JSValue* constrProtoVal = ARG_src5;
int firstArg = ARG_int6;
#ifndef NDEBUG
ConstructData constructData;
ASSERT(ARG_src1->getConstructData(constructData) == ConstructTypeJS);
ASSERT(constructor->getConstructData(constructData) == ConstructTypeJS);
#endif
if (*ARG_profilerReference)
(*ARG_profilerReference)->willExecute(callFrame, constructor);
ScopeChainNode* callDataScopeChain = constructor->m_scopeChain.node();
FunctionBodyNode* functionBodyNode = constructor->m_body.get();
CodeBlock* newCodeBlock = &functionBodyNode->byteCode(callDataScopeChain);
......@@ -4783,8 +4816,8 @@ JSValue* Machine::cti_op_construct_NotJSConstruct(CTI_ARGS)
CallFrame* callFrame = ARG_callFrame;
JSValue* constrVal = ARG_src1;
int firstArg = ARG_int3;
int argCount = ARG_int5;
int argCount = ARG_int3;
int firstArg = ARG_int6;
ConstructData constructData;
ConstructType constructType = constrVal->getConstructData(constructData);
......@@ -4810,7 +4843,7 @@ JSValue* Machine::cti_op_construct_NotJSConstruct(CTI_ARGS)
ASSERT(constructType == ConstructTypeNone);
ARG_globalData->exception = createNotAConstructorError(callFrame, constrVal, ARG_instr6, callFrame->codeBlock());
ARG_globalData->exception = createNotAConstructorError(callFrame, constrVal, ARG_instr4, callFrame->codeBlock());
VM_THROW_EXCEPTION();
}
......
......@@ -188,11 +188,11 @@ namespace JSC {
static void SFX_CALL cti_op_create_arguments(CTI_ARGS);
static void SFX_CALL cti_op_tear_off_activation(CTI_ARGS);
static void SFX_CALL cti_op_tear_off_arguments(CTI_ARGS);
static void SFX_CALL cti_op_ret_profiler(CTI_ARGS);
static void SFX_CALL cti_op_ret_scopeChain(CTI_ARGS);
static JSValue* SFX_CALL cti_op_new_array(CTI_ARGS);
static JSValue* SFX_CALL cti_op_resolve(CTI_ARGS);
static JSValue* SFX_CALL cti_op_resolve_global(CTI_ARGS);
static JSValue* SFX_CALL cti_op_construct_JSConstructFast(CTI_ARGS);
static VoidPtrPair SFX_CALL cti_op_construct_JSConstruct(CTI_ARGS);
static JSValue* SFX_CALL cti_op_construct_NotJSConstruct(CTI_ARGS);
static JSValue* SFX_CALL cti_op_get_by_val(CTI_ARGS);
......@@ -255,8 +255,12 @@ namespace JSC {
static JSValue* SFX_CALL cti_op_new_error(CTI_ARGS);
static void SFX_CALL cti_op_debug(CTI_ARGS);
static void SFX_CALL cti_op_call_profiler(CTI_ARGS);
static void SFX_CALL cti_op_ret_profiler(CTI_ARGS);
static void* SFX_CALL cti_vm_throw(CTI_ARGS);
static void* SFX_CALL cti_vm_compile(CTI_ARGS);
static void* SFX_CALL cti_vm_lazyLinkCall(CTI_ARGS);
static JSValue* SFX_CALL cti_op_push_activation(CTI_ARGS);
#endif // ENABLE(CTI)
......
......@@ -25,6 +25,7 @@
#include "config.h"
#include "JSFunction.h"
#include "CodeBlock.h"
#include "CommonIdentifiers.h"
#include "ExecState.h"
#include "FunctionPrototype.h"
......@@ -51,6 +52,17 @@ JSFunction::JSFunction(ExecState* exec, const Identifier& name, FunctionBodyNode
{
}
JSFunction::~JSFunction()
{
#if ENABLE(CTI)
// JIT code for other functions may have had calls linked directly to the code for this function; these links
// are based on a check for the this pointer value for this JSFunction - which will no longer be valid once
// this memory is freed and may be reused (potentially for another, different JSFunction).
if (m_body.get() && m_body->isGenerated())
m_body->generatedByteCode().unlinkCallers();
#endif
}
void JSFunction::mark()
{
Base::mark();
......
......@@ -45,6 +45,7 @@ namespace JSC {
JSFunction(PassRefPtr<JSC::StructureID> st) : InternalFunction(st), m_scopeChain(NoScopeChain()) {}
public:
JSFunction(ExecState*, const Identifier&, FunctionBodyNode*, ScopeChainNode*);
~JSFunction();
virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&);
virtual void put(ExecState*, const Identifier& propertyName, JSValue*, PutPropertySlot&);
......
......@@ -2278,6 +2278,11 @@ namespace JSC {
return *m_code;
}
bool isGenerated() JSC_FAST_CALL
{
return m_code;
}
void mark();
void finishParsing(const SourceCode&, ParameterNode*);
......
......@@ -1057,6 +1057,11 @@ public:
return dst.m_offset - src.m_offset;
}
static int getDifferenceBetweenLabels(JmpSrc src, JmpDst dst)
{
return dst.m_offset - src.m_offset;
}
static void repatchImmediate(intptr_t where, int32_t value)
{
reinterpret_cast<int32_t*>(where)[-1] = value;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment