⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 194401 in webkit


Ignore:
Timestamp:
Dec 23, 2015, 4:14:13 PM (11 years ago)
Author:
fpizlo@apple.com
Message:

Need a story for platform-specific Args
https://bugs.webkit.org/show_bug.cgi?id=152529

Reviewed by Michael Saboff.

This teaches Arg that some Arg forms are not valid on some targets. The instruction selector now
uses this to avoid immediates and addresses that the target wouldn't like.

This shouldn't change code generation on X86, but is meant as a step towards ARM64 support.

  • b3/B3LowerToAir.cpp:

(JSC::B3::Air::LowerToAir::crossesInterference):
(JSC::B3::Air::LowerToAir::effectiveAddr):
(JSC::B3::Air::LowerToAir::addr):
(JSC::B3::Air::LowerToAir::loadPromise):
(JSC::B3::Air::LowerToAir::imm):
(JSC::B3::Air::LowerToAir::lower):

  • b3/air/AirAllocateStack.cpp:

(JSC::B3::Air::allocateStack):

  • b3/air/AirArg.h:

(JSC::B3::Air::Arg::Arg):
(JSC::B3::Air::Arg::imm):
(JSC::B3::Air::Arg::imm64):
(JSC::B3::Air::Arg::callArg):
(JSC::B3::Air::Arg::isValidScale):
(JSC::B3::Air::Arg::tmpIndex):
(JSC::B3::Air::Arg::withOffset):
(JSC::B3::Air::Arg::isValidImmForm):
(JSC::B3::Air::Arg::isValidAddrForm):
(JSC::B3::Air::Arg::isValidIndexForm):
(JSC::B3::Air::Arg::isValidForm):
(JSC::B3::Air::Arg::forEachTmpFast):

  • b3/air/opcode_generator.rb:
Location:
trunk/Source/JavaScriptCore
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r194400 r194401  
     12015-12-23  Filip Pizlo  <fpizlo@apple.com>
     2
     3        Need a story for platform-specific Args
     4        https://bugs.webkit.org/show_bug.cgi?id=152529
     5
     6        Reviewed by Michael Saboff.
     7
     8        This teaches Arg that some Arg forms are not valid on some targets. The instruction selector now
     9        uses this to avoid immediates and addresses that the target wouldn't like.
     10
     11        This shouldn't change code generation on X86, but is meant as a step towards ARM64 support.
     12
     13        * b3/B3LowerToAir.cpp:
     14        (JSC::B3::Air::LowerToAir::crossesInterference):
     15        (JSC::B3::Air::LowerToAir::effectiveAddr):
     16        (JSC::B3::Air::LowerToAir::addr):
     17        (JSC::B3::Air::LowerToAir::loadPromise):
     18        (JSC::B3::Air::LowerToAir::imm):
     19        (JSC::B3::Air::LowerToAir::lower):
     20        * b3/air/AirAllocateStack.cpp:
     21        (JSC::B3::Air::allocateStack):
     22        * b3/air/AirArg.h:
     23        (JSC::B3::Air::Arg::Arg):
     24        (JSC::B3::Air::Arg::imm):
     25        (JSC::B3::Air::Arg::imm64):
     26        (JSC::B3::Air::Arg::callArg):
     27        (JSC::B3::Air::Arg::isValidScale):
     28        (JSC::B3::Air::Arg::tmpIndex):
     29        (JSC::B3::Air::Arg::withOffset):
     30        (JSC::B3::Air::Arg::isValidImmForm):
     31        (JSC::B3::Air::Arg::isValidAddrForm):
     32        (JSC::B3::Air::Arg::isValidIndexForm):
     33        (JSC::B3::Air::Arg::isValidForm):
     34        (JSC::B3::Air::Arg::forEachTmpFast):
     35        * b3/air/opcode_generator.rb:
     36
    1372015-12-23  Keith Miller  <keith_miller@apple.com>
    238
  • trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp

    r194389 r194401  
    338338
    339339    // This turns the given operand into an address.
    340     Arg effectiveAddr(Value* address)
    341     {
     340    Arg effectiveAddr(Value* address, int32_t offset, Arg::Width width)
     341    {
     342        // B3 allows any memory operation to have a 32-bit offset. That's not how some architectures
     343        // work. We solve this by requiring a just-before-lowering phase that legalizes offsets.
     344        // FIXME: Implement such a legalization phase.
     345        // https://bugs.webkit.org/show_bug.cgi?id=152530
     346        ASSERT(Arg::isValidAddrForm(offset));
     347
     348        auto fallback = [&] () -> Arg {
     349            return Arg::addr(tmp(address), offset);
     350        };
     351       
    342352        static const unsigned lotsOfUses = 10; // This is arbitrary and we should tune it eventually.
    343        
     353
    344354        // Only match if the address value isn't used in some large number of places.
    345355        if (m_useCounts.numUses(address) > lotsOfUses)
    346             return Arg::addr(tmp(address));
     356            return fallback();
    347357       
    348358        switch (address->opcode()) {
     
    351361            Value* right = address->child(1);
    352362
    353             auto tryIndex = [&] (Value* index, Value* offset) -> Arg {
     363            auto tryIndex = [&] (Value* index, Value* base) -> Arg {
    354364                if (index->opcode() != Shl)
    355365                    return Arg();
    356                 if (m_locked.contains(index->child(0)) || m_locked.contains(offset))
     366                if (m_locked.contains(index->child(0)) || m_locked.contains(base))
    357367                    return Arg();
    358368                if (!index->child(1)->hasInt32())
     
    360370               
    361371                unsigned scale = 1 << (index->child(1)->asInt32() & 31);
    362                 if (!Arg::isValidScale(scale))
     372                if (!Arg::isValidIndexForm(scale, offset, width))
    363373                    return Arg();
    364374
    365                 return Arg::index(tmp(offset), tmp(index->child(0)), scale);
     375                return Arg::index(tmp(base), tmp(index->child(0)), scale, offset);
    366376            };
    367377
     
    371381                return result;
    372382
    373             if (m_locked.contains(left) || m_locked.contains(right))
    374                 return Arg::addr(tmp(address));
     383            if (m_locked.contains(left) || m_locked.contains(right)
     384                || !Arg::isValidIndexForm(1, offset, width))
     385                return fallback();
    375386           
    376             return Arg::index(tmp(left), tmp(right));
     387            return Arg::index(tmp(left), tmp(right), 1, offset);
    377388        }
    378389
     
    383394            // amount is greater than 1, then there isn't really anything smart that we could do here.
    384395            // We avoid using baseless indexes because their encoding isn't particularly efficient.
    385             if (m_locked.contains(left) || !address->child(1)->isInt32(1))
    386                 return Arg::addr(tmp(address));
    387 
    388             return Arg::index(tmp(left), tmp(left));
     396            if (m_locked.contains(left) || !address->child(1)->isInt32(1)
     397                || !Arg::isValidIndexForm(1, offset, width))
     398                return fallback();
     399
     400            return Arg::index(tmp(left), tmp(left), 1, offset);
    389401        }
    390402
    391403        case FramePointer:
    392             return Arg::addr(Tmp(GPRInfo::callFrameRegister));
     404            return Arg::addr(Tmp(GPRInfo::callFrameRegister), offset);
    393405
    394406        case B3::StackSlot:
    395             return Arg::stack(m_stackToStack.get(address->as<StackSlotValue>()));
     407            return Arg::stack(m_stackToStack.get(address->as<StackSlotValue>()), offset);
    396408
    397409        default:
    398             return Arg::addr(tmp(address));
     410            return fallback();
    399411        }
    400412    }
     
    408420            return Arg();
    409421
    410         Arg result = effectiveAddr(value->lastChild());
    411         ASSERT(result);
    412        
    413         int32_t offset = memoryValue->as<MemoryValue>()->offset();
    414         Arg offsetResult = result.withOffset(offset);
    415         if (!offsetResult)
    416             return Arg::addr(tmp(value->lastChild()), offset);
    417         return offsetResult;
     422        int32_t offset = value->offset();
     423        Arg::Width width = Arg::widthForBytes(value->accessByteSize());
     424
     425        Arg result = effectiveAddr(value->lastChild(), offset, width);
     426        ASSERT(result.isValidForm(width));
     427
     428        return result;
    418429    }
    419430
     
    436447    Arg imm(Value* value)
    437448    {
    438         if (value->hasInt() && value->representableAs<int32_t>())
    439             return Arg::imm(value->asNumber<int32_t>());
     449        if (value->hasInt()) {
     450            int64_t intValue = value->asInt();
     451            if (Arg::isValidImmForm(intValue))
     452                return Arg::imm(intValue);
     453        }
    440454        return Arg();
    441455    }
     
    17991813        }
    18001814
    1801         case Const32: {
    1802             append(Move, imm(m_value), tmp(m_value));
    1803             return;
    1804         }
     1815        case Const32:
    18051816        case Const64: {
    18061817            if (imm(m_value))
    18071818                append(Move, imm(m_value), tmp(m_value));
    18081819            else
    1809                 append(Move, Arg::imm64(m_value->asInt64()), tmp(m_value));
     1820                append(Move, Arg::imm64(m_value->asInt()), tmp(m_value));
    18101821            return;
    18111822        }
  • trunk/Source/JavaScriptCore/b3/air/AirAllocateStack.cpp

    r194331 r194401  
    232232    // offset-from-FP refers to.
    233233
     234    // FIXME: This may produce addresses that aren't valid if we end up with a ginormous stack frame.
     235    // We would have to scavenge for temporaries if this happened. Fortunately, this case will be
     236    // extremely rare so we can do crazy things when it arises.
     237    // https://bugs.webkit.org/show_bug.cgi?id=152530
     238   
    234239    for (BasicBlock* block : code) {
    235240        for (Inst& inst : *block) {
  • trunk/Source/JavaScriptCore/b3/air/AirArg.h

    r194331 r194401  
    3232#include "B3Common.h"
    3333#include "B3Type.h"
     34#include <wtf/Optional.h>
    3435
    3536namespace JSC { namespace B3 { namespace Air {
     
    5051        Tmp,
    5152
    52         // This is an immediate that the instruction will materialize.
     53        // This is an immediate that the instruction will materialize. Imm is the immediate that can be
     54        // inlined into most instructions, while Imm64 indicates a constant materialization and is
     55        // usually only usable with Move. Specials may also admit it, for example for stackmaps used for
     56        // OSR exit and tail calls.
    5357        Imm,
    5458        Imm64,
     
    328332    }
    329333
    330     static Arg imm(int32_t value)
     334    static Arg imm(int64_t value)
    331335    {
    332336        Arg result;
     
    336340    }
    337341
    338     static Arg imm64(intptr_t value)
     342    static Arg imm64(int64_t value)
    339343    {
    340344        Arg result;
     
    371375    }
    372376
    373     static bool isValidScale(unsigned scale)
     377    // If you don't pass a Width, this optimistically assumes that you're using the right width.
     378    static bool isValidScale(unsigned scale, Optional<Width> width = Nullopt)
    374379    {
    375380        switch (scale) {
    376381        case 1:
     382            if (isX86() || isARM64())
     383                return true;
     384            return false;
    377385        case 2:
    378386        case 4:
    379387        case 8:
    380             return true;
     388            if (isX86())
     389                return true;
     390            if (isARM64()) {
     391                if (!width)
     392                    return true;
     393                return scale == 1 || scale == bytes(*width);
     394            }
     395            return false;
    381396        default:
    382397            return false;
     
    755770    }
    756771
    757     Arg withOffset(int32_t additionalOffset) const
     772    // If 'this' is an address Arg, then it returns a new address Arg with the additional offset applied.
     773    // Note that this does not consider whether doing so produces a valid Arg or not. Unless you really
     774    // know what you're doing, you should call Arg::isValidForm() on the result. Some code won't do that,
     775    // like if you're applying a very small offset to a Arg::stack() that you know has no offset to begin
     776    // with. It's safe to assume that all targets allow small offsets (like, 0..7) for Addr, Stack, and
     777    // CallArg.
     778    Arg withOffset(int64_t additionalOffset) const
    758779    {
    759780        if (!hasOffset())
    760781            return Arg();
    761         if (sumOverflows<int32_t>(offset(), additionalOffset))
     782        if (sumOverflows<int64_t>(offset(), additionalOffset))
    762783            return Arg();
    763784        switch (kind()) {
     
    773794            RELEASE_ASSERT_NOT_REACHED();
    774795            return Arg();
     796        }
     797    }
     798
     799    static bool isValidImmForm(int64_t value)
     800    {
     801        if (isX86())
     802            return B3::isRepresentableAs<int32_t>(value);
     803        // FIXME: ARM has some specific rules about what kinds of immediates are valid.
     804        // https://bugs.webkit.org/show_bug.cgi?id=152530
     805        return false;
     806    }
     807
     808    static bool isValidAddrForm(int32_t offset)
     809    {
     810        if (isX86())
     811            return true;
     812        // FIXME: ARM has some specific rules about what kinds of offsets are valid.
     813        // https://bugs.webkit.org/show_bug.cgi?id=152530
     814        UNUSED_PARAM(offset);
     815        return false;
     816    }
     817
     818    static bool isValidIndexForm(unsigned scale, int32_t offset, Optional<Width> width = Nullopt)
     819    {
     820        if (!isValidScale(scale, width))
     821            return false;
     822        if (isX86())
     823            return true;
     824        if (isARM64())
     825            return !offset;
     826        return false;
     827    }
     828
     829    // If you don't pass a width then this optimistically assumes that you're using the right width. But
     830    // the width is relevant to validity, so passing a null width is only useful for assertions. Don't
     831    // pass null widths when cascading through Args in the instruction selector!
     832    bool isValidForm(Optional<Width> width = Nullopt) const
     833    {
     834        switch (kind()) {
     835        case Invalid:
     836            return false;
     837        case Tmp:
     838            return true;
     839        case Imm:
     840            return isValidImmForm(value());
     841        case Imm64:
     842            return true;
     843        case Addr:
     844        case Stack:
     845        case CallArg:
     846            return isValidAddrForm(offset());
     847        case Index:
     848            return isValidIndexForm(offset(), scale(), width);
     849        case RelCond:
     850        case ResCond:
     851        case DoubleCond:
     852        case Special:
     853            return true;
    775854        }
    776855    }
  • trunk/Source/JavaScriptCore/b3/air/opcode_generator.rb

    r194331 r194401  
    5151        @type = type
    5252        @width = width
     53    end
     54
     55    def widthCode
     56        if width == "Ptr"
     57            "Arg::pointerWidth()"
     58        else
     59            "Arg::Width#{width}"
     60        end
    5361    end
    5462end
     
    645653                end
    646654
    647                 if arg.width == "Ptr"
    648                     width = "Arg::pointerWidth()"
    649                 else
    650                     width = "Arg::Width#{arg.width}"
    651                 end
    652                
    653                 outp.puts "functor(args[#{index}], Arg::#{role}, Arg::#{arg.type}P, #{width});"
     655                outp.puts "functor(args[#{index}], Arg::#{role}, Arg::#{arg.type}P, #{arg.widthCode});"
    654656            }
    655657        end
     
    675677                    | form |
    676678                    notSpecial = (not form.kinds.detect { | kind | kind.special })
    677                     beginArchs(outp, form.archs)
    678                     outp.puts "OPGEN_RETURN(#{notSpecial});"
    679                     endArchs(outp, form.archs)
     679                    if notSpecial
     680                        beginArchs(outp, form.archs)
     681                        outp.puts "OPGEN_RETURN(true);"
     682                        endArchs(outp, form.archs)
     683                    end
    680684                }
    681685                matchForms(outp, :safe, overload.forms, 0, columnGetter, filter, callback)
     
    747751            overload.signature.length.times {
    748752                | index |
    749                 role = overload.signature[index].role
    750                 type = overload.signature[index].type
     753                arg = overload.signature[index]
    751754                kind = form.kinds[index]
    752755                needsMoreValidation |= kind.special
    753                
    754                 # We already know that the form matches. We don't have to validate the role, since
    755                 # kind implies role. So, the only thing left to validate is the type. And we only have
    756                 # to validate the type if we have a Tmp.
    757                 if kind.name == "Tmp"
    758                     outp.puts "if (!args[#{index}].tmp().is#{type}P())"
     756
     757                # Some kinds of Args reqire additional validation.
     758                case kind.name
     759                when "Tmp"
     760                    outp.puts "if (!args[#{index}].tmp().is#{arg.type}P())"
    759761                    outp.puts "OPGEN_RETURN(false);"
     762                when "Imm"
     763                    outp.puts "if (!Arg::isValidImmForm(args[#{index}].value()))"
     764                    outp.puts "OPGEN_RETURN(false);"
     765                when "Addr"
     766                    outp.puts "if (!Arg::isValidAddrForm(args[#{index}].offset()))"
     767                    outp.puts "OPGEN_RETURN(false);"
     768                when "Index"
     769                    outp.puts "if (!Arg::isValidIndexForm(args[#{index}].scale(), args[#{index}].offset(), #{arg.widthCode}))"
     770                    outp.puts "OPGEN_RETURN(false);"
     771                when "Imm64"
     772                when "RelCond"
     773                when "ResCond"
     774                when "DoubleCond"
     775                else
     776                    raise "Unexpected kind: #{kind.name}"
    760777                end
    761778            }
Note: See TracChangeset for help on using the changeset viewer.