Changeset 194401 in webkit
- Timestamp:
- Dec 23, 2015, 4:14:13 PM (11 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 5 edited
-
ChangeLog (modified) (1 diff)
-
b3/B3LowerToAir.cpp (modified) (8 diffs)
-
b3/air/AirAllocateStack.cpp (modified) (1 diff)
-
b3/air/AirArg.h (modified) (7 diffs)
-
b3/air/opcode_generator.rb (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/ChangeLog
r194400 r194401 1 2015-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 1 37 2015-12-23 Keith Miller <keith_miller@apple.com> 2 38 -
trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp
r194389 r194401 338 338 339 339 // 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 342 352 static const unsigned lotsOfUses = 10; // This is arbitrary and we should tune it eventually. 343 353 344 354 // Only match if the address value isn't used in some large number of places. 345 355 if (m_useCounts.numUses(address) > lotsOfUses) 346 return Arg::addr(tmp(address));356 return fallback(); 347 357 348 358 switch (address->opcode()) { … … 351 361 Value* right = address->child(1); 352 362 353 auto tryIndex = [&] (Value* index, Value* offset) -> Arg {363 auto tryIndex = [&] (Value* index, Value* base) -> Arg { 354 364 if (index->opcode() != Shl) 355 365 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)) 357 367 return Arg(); 358 368 if (!index->child(1)->hasInt32()) … … 360 370 361 371 unsigned scale = 1 << (index->child(1)->asInt32() & 31); 362 if (!Arg::isValid Scale(scale))372 if (!Arg::isValidIndexForm(scale, offset, width)) 363 373 return Arg(); 364 374 365 return Arg::index(tmp( offset), tmp(index->child(0)), scale);375 return Arg::index(tmp(base), tmp(index->child(0)), scale, offset); 366 376 }; 367 377 … … 371 381 return result; 372 382 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(); 375 386 376 return Arg::index(tmp(left), tmp(right) );387 return Arg::index(tmp(left), tmp(right), 1, offset); 377 388 } 378 389 … … 383 394 // amount is greater than 1, then there isn't really anything smart that we could do here. 384 395 // 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); 389 401 } 390 402 391 403 case FramePointer: 392 return Arg::addr(Tmp(GPRInfo::callFrameRegister) );404 return Arg::addr(Tmp(GPRInfo::callFrameRegister), offset); 393 405 394 406 case B3::StackSlot: 395 return Arg::stack(m_stackToStack.get(address->as<StackSlotValue>()) );407 return Arg::stack(m_stackToStack.get(address->as<StackSlotValue>()), offset); 396 408 397 409 default: 398 return Arg::addr(tmp(address));410 return fallback(); 399 411 } 400 412 } … … 408 420 return Arg(); 409 421 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; 418 429 } 419 430 … … 436 447 Arg imm(Value* value) 437 448 { 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 } 440 454 return Arg(); 441 455 } … … 1799 1813 } 1800 1814 1801 case Const32: { 1802 append(Move, imm(m_value), tmp(m_value)); 1803 return; 1804 } 1815 case Const32: 1805 1816 case Const64: { 1806 1817 if (imm(m_value)) 1807 1818 append(Move, imm(m_value), tmp(m_value)); 1808 1819 else 1809 append(Move, Arg::imm64(m_value->asInt 64()), tmp(m_value));1820 append(Move, Arg::imm64(m_value->asInt()), tmp(m_value)); 1810 1821 return; 1811 1822 } -
trunk/Source/JavaScriptCore/b3/air/AirAllocateStack.cpp
r194331 r194401 232 232 // offset-from-FP refers to. 233 233 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 234 239 for (BasicBlock* block : code) { 235 240 for (Inst& inst : *block) { -
trunk/Source/JavaScriptCore/b3/air/AirArg.h
r194331 r194401 32 32 #include "B3Common.h" 33 33 #include "B3Type.h" 34 #include <wtf/Optional.h> 34 35 35 36 namespace JSC { namespace B3 { namespace Air { … … 50 51 Tmp, 51 52 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. 53 57 Imm, 54 58 Imm64, … … 328 332 } 329 333 330 static Arg imm(int 32_t value)334 static Arg imm(int64_t value) 331 335 { 332 336 Arg result; … … 336 340 } 337 341 338 static Arg imm64(int ptr_t value)342 static Arg imm64(int64_t value) 339 343 { 340 344 Arg result; … … 371 375 } 372 376 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) 374 379 { 375 380 switch (scale) { 376 381 case 1: 382 if (isX86() || isARM64()) 383 return true; 384 return false; 377 385 case 2: 378 386 case 4: 379 387 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; 381 396 default: 382 397 return false; … … 755 770 } 756 771 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 758 779 { 759 780 if (!hasOffset()) 760 781 return Arg(); 761 if (sumOverflows<int 32_t>(offset(), additionalOffset))782 if (sumOverflows<int64_t>(offset(), additionalOffset)) 762 783 return Arg(); 763 784 switch (kind()) { … … 773 794 RELEASE_ASSERT_NOT_REACHED(); 774 795 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; 775 854 } 776 855 } -
trunk/Source/JavaScriptCore/b3/air/opcode_generator.rb
r194331 r194401 51 51 @type = type 52 52 @width = width 53 end 54 55 def widthCode 56 if width == "Ptr" 57 "Arg::pointerWidth()" 58 else 59 "Arg::Width#{width}" 60 end 53 61 end 54 62 end … … 645 653 end 646 654 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});" 654 656 } 655 657 end … … 675 677 | form | 676 678 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 680 684 } 681 685 matchForms(outp, :safe, overload.forms, 0, columnGetter, filter, callback) … … 747 751 overload.signature.length.times { 748 752 | index | 749 role = overload.signature[index].role 750 type = overload.signature[index].type 753 arg = overload.signature[index] 751 754 kind = form.kinds[index] 752 755 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())" 759 761 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}" 760 777 end 761 778 }
Note:
See TracChangeset
for help on using the changeset viewer.