Ignore:
Timestamp:
Feb 8, 2017, 1:21:45 PM (8 years ago)
Author:
[email protected]
Message:

Air IRC might spill a terminal that produces a value after the terminal
https://bugs.webkit.org/show_bug.cgi?id=167919
<rdar://problem/29754721>

Reviewed by Filip Pizlo.

IRC may spill a value-producing terminal (a patchpoint can be a value-producing terminal).
It used to do this by placing the spill *after* the terminal. This produces an invalid
graph because no instructions are allowed after the terminal.

I fixed this bug by having a cleanup pass over the IR after IRC is done.
The pass detects this problem, and fixes it by moving the spill into the
successors. However, it is careful to detect when the edge to the
successor is a critical edge. If the value-producing patchpoint is
the only predecessor of the successor, it just moves the spill
code to the beginning of the successor. Otherwise, it's a critical
edge and it breaks it by adding a block that does the spilling then
jumps to the successor.

  • b3/air/AirInsertionSet.cpp:
  • b3/air/AirInsertionSet.h:

(JSC::B3::Air::InsertionSet::insertInsts):

  • b3/air/AirIteratedRegisterCoalescing.cpp:
  • b3/testb3.cpp:

(JSC::B3::testTerminalPatchpointThatNeedsToBeSpilled):
(JSC::B3::testTerminalPatchpointThatNeedsToBeSpilled2):
(JSC::B3::run):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/b3/testb3.cpp

    r210919 r211896  
    11/*
    2  * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
     2 * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    1337613376    CHECK_EQ(terminal.args[1].kind(), firstKind);
    1337713377    CHECK(terminal.args[2].kind() == Air::Arg::BitImm || terminal.args[2].kind() == Air::Arg::BitImm64);
     13378}
     13379
     13380void testTerminalPatchpointThatNeedsToBeSpilled()
     13381{
     13382    // This is a unit test for how FTL's heap allocation fast paths behave.
     13383    Procedure proc;
     13384   
     13385    BasicBlock* root = proc.addBlock();
     13386    BasicBlock* success = proc.addBlock();
     13387    BasicBlock* slowPath = proc.addBlock();
     13388   
     13389    PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin());
     13390    patchpoint->effects.terminal = true;
     13391    patchpoint->clobber(RegisterSet::macroScratchRegisters());
     13392   
     13393    root->appendSuccessor(success);
     13394    root->appendSuccessor(FrequentedBlock(slowPath, FrequencyClass::Rare));
     13395   
     13396    patchpoint->setGenerator(
     13397        [&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
     13398            AllowMacroScratchRegisterUsage allowScratch(jit);
     13399            jit.move(CCallHelpers::TrustedImm32(42), params[0].gpr());
     13400           
     13401            CCallHelpers::Jump jumpToSuccess;
     13402            if (!params.fallsThroughToSuccessor(0))
     13403                jumpToSuccess = jit.jump();
     13404           
     13405            Vector<Box<CCallHelpers::Label>> labels = params.successorLabels();
     13406           
     13407            params.addLatePath(
     13408                [=] (CCallHelpers& jit) {
     13409                    if (jumpToSuccess.isSet())
     13410                        jumpToSuccess.linkTo(*labels[0], &jit);
     13411                });
     13412        });
     13413   
     13414    Vector<Value*> args;
     13415    {
     13416        RegisterSet fillAllGPRsSet = RegisterSet::allGPRs();
     13417        fillAllGPRsSet.exclude(RegisterSet::stackRegisters());
     13418        fillAllGPRsSet.exclude(RegisterSet::reservedHardwareRegisters());
     13419
     13420        for (unsigned i = 0; i < fillAllGPRsSet.numberOfSetRegisters(); i++)
     13421            args.append(success->appendNew<Const32Value>(proc, Origin(), i));
     13422    }
     13423
     13424    {
     13425        // Now force all values into every available register.
     13426        PatchpointValue* p = success->appendNew<PatchpointValue>(proc, Void, Origin());
     13427        for (Value* v : args)
     13428            p->append(v, ValueRep::SomeRegister);
     13429        p->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { });
     13430    }
     13431
     13432    {
     13433        // Now require the original patchpoint to be materialized into a register.
     13434        PatchpointValue* p = success->appendNew<PatchpointValue>(proc, Void, Origin());
     13435        p->append(patchpoint, ValueRep::SomeRegister);
     13436        p->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { });
     13437    }
     13438
     13439    success->appendNew<Value>(proc, Return, Origin(), success->appendNew<Const32Value>(proc, Origin(), 10));
     13440   
     13441    slowPath->appendNew<Value>(proc, Return, Origin(), slowPath->appendNew<Const32Value>(proc, Origin(), 20));
     13442   
     13443    auto code = compile(proc);
     13444    CHECK_EQ(invoke<int>(*code), 10);
     13445}
     13446
     13447void testTerminalPatchpointThatNeedsToBeSpilled2()
     13448{
     13449    // This is a unit test for how FTL's heap allocation fast paths behave.
     13450    Procedure proc;
     13451   
     13452    BasicBlock* root = proc.addBlock();
     13453    BasicBlock* one = proc.addBlock();
     13454    BasicBlock* success = proc.addBlock();
     13455    BasicBlock* slowPath = proc.addBlock();
     13456
     13457    Value* arg = root->appendNew<Value>(
     13458        proc, Trunc, Origin(),
     13459        root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
     13460
     13461    root->appendNew<Value>(
     13462        proc, Branch, Origin(), arg);
     13463    root->appendSuccessor(one);
     13464    root->appendSuccessor(FrequentedBlock(slowPath, FrequencyClass::Rare));
     13465   
     13466    PatchpointValue* patchpoint = one->appendNew<PatchpointValue>(proc, Int32, Origin());
     13467    patchpoint->effects.terminal = true;
     13468    patchpoint->clobber(RegisterSet::macroScratchRegisters());
     13469    patchpoint->append(arg, ValueRep::SomeRegister);
     13470   
     13471    one->appendSuccessor(success);
     13472    one->appendSuccessor(FrequentedBlock(slowPath, FrequencyClass::Rare));
     13473   
     13474    patchpoint->setGenerator(
     13475        [&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
     13476            AllowMacroScratchRegisterUsage allowScratch(jit);
     13477            jit.move(CCallHelpers::TrustedImm32(666), params[0].gpr());
     13478            auto goToFastPath = jit.branch32(CCallHelpers::Equal, params[1].gpr(), CCallHelpers::TrustedImm32(42));
     13479            auto jumpToSlow = jit.jump();
     13480           
     13481            // Make sure the asserts here pass.
     13482            params.fallsThroughToSuccessor(0);
     13483            params.fallsThroughToSuccessor(1);
     13484
     13485            Vector<Box<CCallHelpers::Label>> labels = params.successorLabels();
     13486           
     13487            params.addLatePath(
     13488                [=] (CCallHelpers& jit) {
     13489                    goToFastPath.linkTo(*labels[0], &jit);
     13490                    jumpToSlow.linkTo(*labels[1], &jit);
     13491                });
     13492        });
     13493   
     13494    Vector<Value*> args;
     13495    {
     13496        RegisterSet fillAllGPRsSet = RegisterSet::allGPRs();
     13497        fillAllGPRsSet.exclude(RegisterSet::stackRegisters());
     13498        fillAllGPRsSet.exclude(RegisterSet::reservedHardwareRegisters());
     13499
     13500        for (unsigned i = 0; i < fillAllGPRsSet.numberOfSetRegisters(); i++)
     13501            args.append(success->appendNew<Const32Value>(proc, Origin(), i));
     13502    }
     13503
     13504    {
     13505        // Now force all values into every available register.
     13506        PatchpointValue* p = success->appendNew<PatchpointValue>(proc, Void, Origin());
     13507        for (Value* v : args)
     13508            p->append(v, ValueRep::SomeRegister);
     13509        p->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { });
     13510    }
     13511
     13512    {
     13513        // Now require the original patchpoint to be materialized into a register.
     13514        PatchpointValue* p = success->appendNew<PatchpointValue>(proc, Void, Origin());
     13515        p->append(patchpoint, ValueRep::SomeRegister);
     13516        p->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { });
     13517    }
     13518
     13519    success->appendNew<Value>(proc, Return, Origin(), patchpoint);
     13520   
     13521    slowPath->appendNew<Value>(proc, Return, Origin(), arg);
     13522   
     13523    auto original1 = Options::maxB3TailDupBlockSize();
     13524    auto original2 = Options::maxB3TailDupBlockSuccessors();
     13525
     13526    // Tail duplication will break the critical edge we're trying to test because it
     13527    // will clone the slowPath block for both edges to it!
     13528    Options::maxB3TailDupBlockSize() = 0;
     13529    Options::maxB3TailDupBlockSuccessors() = 0;
     13530
     13531    auto code = compile(proc);
     13532    CHECK_EQ(invoke<int>(*code, 1), 1);
     13533    CHECK_EQ(invoke<int>(*code, 0), 0);
     13534    CHECK_EQ(invoke<int>(*code, 42), 666);
     13535
     13536    Options::maxB3TailDupBlockSize() = original1;
     13537    Options::maxB3TailDupBlockSuccessors() = original2;
    1337813538}
    1337913539
     
    1426114421        return !filter || !!strcasestr(testName, filter);
    1426214422    };
     14423
     14424    // We run this test first because it fiddles with some
     14425    // JSC options.
     14426    testTerminalPatchpointThatNeedsToBeSpilled2();
    1426314427
    1426414428    RUN(test42());
     
    1564715811    RUN(testPatchpointTerminalReturnValue(true));
    1564815812    RUN(testPatchpointTerminalReturnValue(false));
     15813    RUN(testTerminalPatchpointThatNeedsToBeSpilled());
    1564915814
    1565015815    RUN(testMemoryFence());
Note: See TracChangeset for help on using the changeset viewer.