Ignore:
Timestamp:
Aug 16, 2017, 1:38:57 PM (8 years ago)
Author:
[email protected]
Message:

Enhance MacroAssembler::probe() to support an initializeStackFunction callback.
https://bugs.webkit.org/show_bug.cgi?id=175617
<rdar://problem/33912104>

Reviewed by JF Bastien.

This patch adds a new feature to MacroAssembler::probe() where the probe function
can provide a ProbeFunction callback to fill in stack values after the stack
pointer has been adjusted. The probe function can use this feature as follows:

  1. Set the new sp value in the ProbeContext's CPUState.
  1. Set the ProbeContext's initializeStackFunction to a ProbeFunction callback which will do the work of filling in the stack values after the probe trampoline has adjusted the machine stack pointer.
  1. Set the ProbeContext's initializeStackArgs to any value that the client wants to pass to the initializeStackFunction callback.
  1. Return from the probe function.

Upon returning from the probe function, the probe trampoline will adjust the
the stack pointer based on the sp value in CPUState. If initializeStackFunction
is not set, the probe trampoline will restore registers and return to its caller.

If initializeStackFunction is set, the trampoline will move the ProbeContext
beyond the range of the stack pointer i.e. it will place the new ProbeContext at
an address lower than where CPUState.sp() points. This ensures that the
ProbeContext will not be trashed by the initializeStackFunction when it writes to
the stack. Then, the trampoline will call back to the initializeStackFunction
ProbeFunction to let it fill in the stack values as desired. The
initializeStackFunction ProbeFunction will be passed the moved ProbeContext at
the new location.

initializeStackFunction may now write to the stack at addresses greater or
equal to CPUState.sp(), but not below that. initializeStackFunction is also
not allowed to change CPUState.sp(). If the initializeStackFunction does not
abide by these rules, then behavior is undefined, and bad things may happen.

For future reference, some implementation details that this patch needed to
be mindful of:

  1. When the probe trampoline allocates stack space for the ProbeContext, it should include OUT_SIZE as well. This ensures that it doesn't have to move the ProbeContext on exit if the probe function didn't change the sp.
  1. If the trampoline has to move the ProbeContext, it needs to point the machine sp to new ProbeContext first before copying over the ProbeContext data. This protects the new ProbeContext from possibly being trashed by interrupts.
  1. When computing the new address of ProbeContext to move to, we need to make sure that it is properly aligned in accordance with stack ABI requirements (just like we did when we allocated the ProbeContext on entry to the probe trampoline).
  1. When copying the ProbeContext to its new location, the trampoline should always copy words from low addresses to high addresses. This is because if we're moving the ProbeContext, we'll always be moving it to a lower address.
  • assembler/MacroAssembler.h:
  • assembler/MacroAssemblerARM.cpp:
  • assembler/MacroAssemblerARM64.cpp:
  • assembler/MacroAssemblerARMv7.cpp:
  • assembler/MacroAssemblerX86Common.cpp:
  • assembler/testmasm.cpp:

(JSC::testProbePreservesGPRS):
(JSC::testProbeModifiesStackPointer):
(JSC::fillStack):
(JSC::testProbeModifiesStackWithCallback):
(JSC::run):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/assembler/testmasm.cpp

    r220744 r220807  
    6464namespace {
    6565
     66using CPUState = MacroAssembler::CPUState;
     67
    6668StaticLock crashLock;
    6769
     
    268270    // to validate that the probe preserves register values.
    269271    unsigned probeCallCount = 0;
    270     MacroAssembler::CPUState originalState;
     272    CPUState originalState;
    271273
    272274    compileAndRun<void>([&] (CCallHelpers& jit) {
     
    348350{
    349351    unsigned probeCallCount = 0;
    350     MacroAssembler::CPUState originalState;
     352    CPUState originalState;
    351353    void* originalSP { nullptr };
    352354    void* modifiedSP { nullptr };
     
    509511}
    510512
     513struct FillStackData {
     514    CPUState originalState;
     515    void* originalSP { nullptr };
     516    void* newSP { nullptr };
     517    uintptr_t modifiedFlags { 0 };
     518    MacroAssembler::SPRegisterID flagsSPR;
     519};
     520
     521static void fillStack(ProbeContext* context)
     522{
     523    auto& cpu = context->cpu;
     524
     525    FillStackData& data = *reinterpret_cast<FillStackData*>(context->initializeStackArg);
     526    CPUState& originalState = data.originalState;
     527    void*& originalSP = data.originalSP;
     528    void*& newSP = data.newSP;
     529    uintptr_t& modifiedFlags = data.modifiedFlags;
     530    MacroAssembler::SPRegisterID& flagsSPR = data.flagsSPR;
     531
     532    CHECK_EQ(reinterpret_cast<void*>(context->initializeStackFunction), reinterpret_cast<void*>(fillStack));
     533    CHECK_EQ(cpu.sp(), newSP);
     534
     535    // Verify that the probe has put the ProbeContext out of harm's way.
     536    CHECK_EQ((reinterpret_cast<void*>(context + 1) <= cpu.sp()), true);
     537
     538    // Verify the CPU state.
     539    for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
     540        if (isFP(id)) {
     541            CHECK_EQ(cpu.gpr(id), originalState.gpr(id));
     542            continue;
     543        }
     544        if (isSpecialGPR(id))
     545            continue;
     546        CHECK_EQ(cpu.gpr(id), testWord(id));
     547    }
     548    for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
     549        CHECK_EQ(cpu.fpr<uint64_t>(id), testWord64(id));
     550    CHECK_EQ(cpu.spr(flagsSPR), modifiedFlags);
     551
     552    // Fill the stack with values.
     553    uintptr_t* p = reinterpret_cast<uintptr_t*>(newSP);
     554    int count = 0;
     555    while (p < reinterpret_cast<uintptr_t*>(originalSP))
     556        *p++ = testWord(count++);
     557};
     558
     559void testProbeModifiesStackWithCallback()
     560{
     561    unsigned probeCallCount = 0;
     562    FillStackData data;
     563    CPUState& originalState = data.originalState;
     564    void*& originalSP = data.originalSP;
     565    void*& newSP = data.newSP;
     566    uintptr_t& modifiedFlags = data.modifiedFlags;
     567    size_t numberOfExtraEntriesToWrite = 10; // ARM64 requires that this be 2 word aligned.
     568    MacroAssembler::SPRegisterID& flagsSPR = data.flagsSPR;
     569
     570#if CPU(X86) || CPU(X86_64)
     571    flagsSPR = X86Registers::eflags;
     572    uintptr_t flagsMask = 0xc5;
     573#elif CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL)
     574    flagsSPR = ARMRegisters::apsr;
     575    uintptr_t flagsMask = 0xf0000000;
     576#elif CPU(ARM64)
     577    flagsSPR = ARM64Registers::nzcv;
     578    uintptr_t flagsMask = 0xf0000000;
     579#endif
     580
     581    compileAndRun<void>([&] (CCallHelpers& jit) {
     582        jit.emitFunctionPrologue();
     583
     584        // Write expected values into the registers.
     585        jit.probe([&] (ProbeContext* context) {
     586            auto& cpu = context->cpu;
     587            probeCallCount++;
     588
     589            // Preserve the original CPU state.
     590            for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
     591                originalState.gpr(id) = cpu.gpr(id);
     592                if (isSpecialGPR(id))
     593                    continue;
     594                cpu.gpr(id) = testWord(static_cast<int>(id));
     595            }
     596            for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id)) {
     597                originalState.fpr(id) = cpu.fpr(id);
     598                cpu.fpr(id) = bitwise_cast<double>(testWord64(id));
     599            }
     600            originalState.spr(flagsSPR) = cpu.spr(flagsSPR);
     601            modifiedFlags = originalState.spr(flagsSPR) ^ flagsMask;
     602            cpu.spr(flagsSPR) = modifiedFlags;
     603
     604            CHECK_EQ(reinterpret_cast<void*>(context->initializeStackFunction), 0);
     605
     606            // Prepare for initializeStack callback.
     607            context->initializeStackFunction = fillStack;
     608            context->initializeStackArg = &data;
     609
     610            // Ensure that we'll be writing over the regions of the stack where the ProbeContext is.
     611            originalSP = cpu.sp();
     612            newSP = reinterpret_cast<uintptr_t*>(context) - numberOfExtraEntriesToWrite;
     613            cpu.sp() = newSP;
     614        });
     615
     616        // Validate that the registers and stack have the expected values.
     617        jit.probe([&] (ProbeContext* context) {
     618            auto& cpu = context->cpu;
     619            probeCallCount++;
     620
     621            // Validate the register values.
     622            for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
     623                if (isFP(id)) {
     624                    CHECK_EQ(cpu.gpr(id), originalState.gpr(id));
     625                    continue;
     626                }
     627                if (isSpecialGPR(id))
     628                    continue;
     629                CHECK_EQ(cpu.gpr(id), testWord(id));
     630            }
     631            for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
     632                CHECK_EQ(cpu.fpr<uint64_t>(id), testWord64(id));
     633            CHECK_EQ(cpu.spr(flagsSPR), modifiedFlags);
     634            CHECK_EQ(cpu.sp(), newSP);
     635
     636            // Validate the stack with values.
     637            uintptr_t* p = reinterpret_cast<uintptr_t*>(newSP);
     638            int count = 0;
     639            while (p < reinterpret_cast<uintptr_t*>(originalSP))
     640                CHECK_EQ(*p++, testWord(count++));
     641        });
     642
     643        // Restore the original state.
     644        jit.probe([&] (ProbeContext* context) {
     645            auto& cpu = context->cpu;
     646            probeCallCount++;
     647            for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
     648                if (isSpecialGPR(id))
     649                    continue;
     650                cpu.gpr(id) = originalState.gpr(id);
     651            }
     652            for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
     653                cpu.fpr(id) = originalState.fpr(id);
     654            cpu.spr(flagsSPR) = originalState.spr(flagsSPR);
     655            cpu.sp() = originalSP;
     656        });
     657
     658        jit.emitFunctionEpilogue();
     659        jit.ret();
     660    });
     661
     662    CHECK_EQ(probeCallCount, 3);
     663}
     664
    511665#define RUN(test) do {                          \
    512666        if (!shouldRun(#test))                  \
     
    541695    RUN(testProbeModifiesStackPointerToNBytesBelowSP());
    542696    RUN(testProbeModifiesProgramCounter());
     697    RUN(testProbeModifiesStackWithCallback());
    543698
    544699    if (tasks.isEmpty())
Note: See TracChangeset for help on using the changeset viewer.