Ignore:
Timestamp:
Jun 22, 2022, 7:56:36 PM (3 years ago)
Author:
[email protected]
Message:

Change offlineasm to emit more efficient LLInt code.
https://bugs.webkit.org/show_bug.cgi?id=241856

Reviewed by Yusuke Suzuki.

  1. Ruby treats numeric 0 as truthy. However, there's a test in arm64LowerMalformedLoadStoreAddresses which assumes a value of 0 would be false. As a result, we see offlineasm emit inefficient LLInt code like this:

".loc 3 821\n" "movz x16, #0 \n" LowLevelInterpreter64.asm:821

"add x13, x3, x16 \n"
"ldr x0, [x13] \n"

... instead of this:

".loc 3 821\n" "ldr x0, [x3] \n" LowLevelInterpreter64.asm:821

This patch fixes this.

  1. offlineasm's emitARM64MoveImmediate chooses to use movn instead of movz based on whether a 64-bit value is negative or not. Instead, it should be making that decision based on the number of halfwords (16-bits) in the value that is 0xffff vs 0. As a result, offlineasm emits code like this:

".loc 1 1638\n" "movn x27, #1, lsl #48 \n" LowLevelInterpreter.asm:1638

"movk x27, #0, lsl #32 \n"
"movk x27, #0, lsl #16 \n"
"movk x27, #0 \n"

... instead of this:

".loc 1 1638\n" "movz x27, #65534, lsl #48 \n" LowLevelInterpreter.asm:1638

This patch fixes this.

  1. offlineasm is trivially assuming the range of immediate offsets for ldr/str instructions is [-255..4095]. However, that's only the range for byte sized load-stores. For 32-bit, the range is actually [-255..16380]. For 64-bit, the range is actually [-255..32760]. As a result,

offlineasm emits code like this:
".loc 1 633\n" "movn x16, #16383 \n" LowLevelInterpreter.asm:633
".loc 1 1518\n" "and x3, x3, x16 \n"
LowLevelInterpreter.asm:1518
".loc 1 1519\n" "movz x16, #16088 \n" LowLevelInterpreter.asm:1519

"add x17, x3, x16 \n"
"ldr x3, [x17] \n"

... instead of this:

".loc 1 633\n" "movn x17, #16383 \n" LowLevelInterpreter.asm:633
".loc 1 1518\n" "and x3, x3, x17 \n"
LowLevelInterpreter.asm:1518
".loc 1 1519\n" "ldr x3, [x3, #16088] \n" LowLevelInterpreter.asm:1519

This patch fixes this for 64-bit and 32-bit load-stores. 16-bit load-stores also has a wider
range, but for now, it will continue to use the conservative range.

This patch also introduces an isMalformedArm64LoadAStoreAddress so that this range check can be
done consistently in all the places that checks for it.

  1. offlineasm is eagerly emitting no-op arguments in instructions, e.g. "lsl #0", and adding 0. As a result, offlineasm emits code like this:

".loc 3 220\n" "movz x13, #51168, lsl #0 \n" LowLevelInterpreter64.asm:220

"add x17, x1, x13, lsl #0 \n"
"ldr w4, [x17, #0] \n"

... instead of this:

".loc 3 220\n" "movz x13, #51168 \n" LowLevelInterpreter64.asm:220

"add x17, x1, x13 \n"
"ldr w4, [x17] \n"

This unnecessary arguments are actually very common throughout the emitted LLIntAssembly.h.

This patch removes these unnecessary arguments, which makes the emitted LLInt code more human
readable due to less clutter.

This patch has passed the testapi and JSC stress tests with a Release build on an M1 Mac.

I also manually verified that the emitARM64MoveImmediate code is working properly by
hacking up LowLevelInterpreter64.asm to emit moves of constants of different values in
the ranges, and for load-store instructions of different sizes, and visually inspecting
the emitted code.

  • Source/JavaScriptCore/offlineasm/arm64.rb:

Canonical link: https://commits.webkit.org/251771@main

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/offlineasm/arm64.rb

    r294787 r295766  
    228228class Address
    229229    def arm64Operand(kind)
    230         raise "Invalid offset #{offset.value} at #{codeOriginString}" if offset.value < -255 or offset.value > 4095
    231         "[#{base.arm64Operand(:quad)}, \##{offset.value}]"
     230        case kind
     231        when :quad, :ptr, :double
     232            if $currentSettings["ADDRESS64"]
     233                raise "Invalid offset #{offset.value} at #{codeOriginString}" if offset.value < -255 or offset.value > 32760
     234            else
     235                raise "Invalid offset #{offset.value} at #{codeOriginString}" if offset.value < -255 or offset.value > 16380
     236            end
     237        when :word, :int, :float
     238            raise "Invalid offset #{offset.value} at #{codeOriginString}" if offset.value < -255 or offset.value > 16380
     239        else
     240            raise "Invalid offset #{offset.value} at #{codeOriginString}" if offset.value < -255 or offset.value > 4095
     241        end
     242        offset.value.zero? \
     243            ? "[#{base.arm64Operand(:quad)}]"
     244            : "[#{base.arm64Operand(:quad)}, \##{offset.value}]"
    232245    end
    233246
     
    238251   
    239252    def arm64EmitLea(destination, kind)
    240         $asm.puts "add #{destination.arm64Operand(kind)}, #{base.arm64Operand(kind)}, \##{offset.value}"
     253        offset.value.zero? \
     254            ? ($asm.puts "mov #{destination.arm64Operand(kind)}, #{base.arm64Operand(kind)}")
     255            : ($asm.puts "add #{destination.arm64Operand(kind)}, #{base.arm64Operand(kind)}, \##{offset.value}")
    241256    end
    242257end
     
    245260    def arm64Operand(kind)
    246261        raise "Invalid offset #{offset.value} at #{codeOriginString}" if offset.value != 0
    247         "[#{base.arm64Operand(:quad)}, #{index.arm64Operand(:quad)}, lsl \##{scaleShift}]"
     262        scaleShift.zero? \
     263            ? "[#{base.arm64Operand(:quad)}, #{index.arm64Operand(:quad)}]"
     264            : "[#{base.arm64Operand(:quad)}, #{index.arm64Operand(:quad)}, lsl \##{scaleShift}]"
    248265    end
    249266
    250267    def arm64EmitLea(destination, kind)
    251         $asm.puts "add #{destination.arm64Operand(kind)}, #{base.arm64Operand(kind)}, #{index.arm64Operand(kind)}, lsl \##{scaleShift}"
     268        scaleShift.zero? \
     269            ? ($asm.puts "add #{destination.arm64Operand(kind)}, #{base.arm64Operand(kind)}, #{index.arm64Operand(kind)}")
     270            : ($asm.puts "add #{destination.arm64Operand(kind)}, #{base.arm64Operand(kind)}, #{index.arm64Operand(kind)}, lsl \##{scaleShift}")
    252271    end
    253272end
     
    265284#
    266285
     286def isMalformedArm64LoadAStoreAddress(opcode, operand)
     287    malformed = false
     288    if operand.is_a? Address
     289        case opcode
     290        when "loadp", "storep", "loadq", "storeq"
     291            if $currentSettings["ADDRESS64"]
     292                malformed ||= (not (-255..32760).include? operand.offset.value)
     293                malformed ||= (not (operand.offset.value % 8).zero?)
     294            else
     295                malformed ||= (not (-255..16380).include? operand.offset.value)
     296            end
     297        when "loadd", "stored"
     298            malformed ||= (not (-255..32760).include? operand.offset.value)
     299            malformed ||= (not (operand.offset.value % 8).zero?)
     300        when "loadi", "loadis", "storei"
     301            malformed ||= (not (-255..16380).include? operand.offset.value)
     302        else
     303            # This is just a conservative estimate of the max offset.
     304            malformed ||= (not (-255..4095).include? operand.offset.value)
     305        end
     306    end
     307    malformed
     308end
     309
    267310def arm64LowerMalformedLoadStoreAddresses(list)
    268311    newList = []
    269 
    270     def isAddressMalformed(opcode, operand)
    271         malformed = false
    272         if operand.is_a? Address
    273             malformed ||= (not (-255..4095).include? operand.offset.value)
    274             if opcode =~ /q$/ and $currentSettings["ADDRESS64"]
    275                 malformed ||= operand.offset.value % 8
    276             end
    277         end
    278         malformed
    279     end
    280312
    281313    list.each {
    282314        | node |
    283315        if node.is_a? Instruction
    284             if node.opcode =~ /^store/ and isAddressMalformed(node.opcode, node.operands[1])
     316            if node.opcode =~ /^store/ and isMalformedArm64LoadAStoreAddress(node.opcode, node.operands[1])
    285317                address = node.operands[1]
    286318                tmp = Tmp.new(codeOrigin, :gpr)
    287319                newList << Instruction.new(node.codeOrigin, "move", [address.offset, tmp])
    288320                newList << Instruction.new(node.codeOrigin, node.opcode, [node.operands[0], BaseIndex.new(node.codeOrigin, address.base, tmp, Immediate.new(codeOrigin, 1), Immediate.new(codeOrigin, 0))], node.annotation)
    289             elsif node.opcode =~ /^load/ and isAddressMalformed(node.opcode, node.operands[0])
     321            elsif node.opcode =~ /^load/ and isMalformedArm64LoadAStoreAddress(node.opcode, node.operands[0])
    290322                address = node.operands[0]
    291323                tmp = Tmp.new(codeOrigin, :gpr)
     
    400432                    (node.opcode =~ /^lea/ or address.scale == 1 or address.scale == size)
    401433            elsif address.is_a? Address
    402                 (-255..4095).include? address.offset.value
     434                not isMalformedArm64LoadAStoreAddress(node.opcode, address)
    403435            else
    404436                false
     
    444476            | node, address |
    445477            case node.opcode
    446             when /^load/
    447                 true
    448             when /^store/
    449                 not (address.is_a? Address and address.offset.value < 0)
     478            when /^load/, /^store/
     479                not address.is_a? Address or not isMalformedArm64LoadAStoreAddress(node.opcode, address)
    450480            when /^lea/
    451481                true
     
    656686
    657687def emitARM64MoveImmediate(value, target)
    658     first = true
    659     isNegative = value < 0
     688    numberOfFilledHalfWords = 0
     689    numberOfZeroHalfWords = 0
    660690    [48, 32, 16, 0].each {
    661691        | shift |
    662692        currentValue = (value >> shift) & 0xffff
    663         next if currentValue == (isNegative ? 0xffff : 0) and (shift != 0 or !first)
     693        if currentValue == 0xffff
     694            numberOfFilledHalfWords += 1
     695        end
     696        if currentValue == 0
     697            numberOfZeroHalfWords += 1
     698        end
     699    }
     700    fillOtherHalfWordsWithOnes = (numberOfFilledHalfWords > numberOfZeroHalfWords)
     701
     702    first = true
     703    [48, 32, 16, 0].each {
     704        | shift |
     705        currentValue = (value >> shift) & 0xffff
     706        next if currentValue == (fillOtherHalfWordsWithOnes ? 0xffff : 0) and (shift != 0 or !first)
    664707        if first
    665             if isNegative
    666                 $asm.puts "movn #{target.arm64Operand(:quad)}, \##{(~currentValue) & 0xffff}, lsl \##{shift}"
    667             else
    668                 $asm.puts "movz #{target.arm64Operand(:quad)}, \##{currentValue}, lsl \##{shift}"
     708            if fillOtherHalfWordsWithOnes
     709                shift.zero? \
     710                    ? ($asm.puts "movn #{target.arm64Operand(:quad)}, \##{(~currentValue) & 0xffff}")
     711                    : ($asm.puts "movn #{target.arm64Operand(:quad)}, \##{(~currentValue) & 0xffff}, lsl \##{shift}")
     712            else
     713                shift.zero? \
     714                    ? ($asm.puts "movz #{target.arm64Operand(:quad)}, \##{currentValue}")
     715                    : ($asm.puts "movz #{target.arm64Operand(:quad)}, \##{currentValue}, lsl \##{shift}")
    669716            end
    670717            first = false
    671718        else
    672             $asm.puts "movk #{target.arm64Operand(:quad)}, \##{currentValue}, lsl \##{shift}"
     719            shift.zero? \
     720                ? ($asm.puts "movk #{target.arm64Operand(:quad)}, \##{currentValue}")
     721                : ($asm.puts "movk #{target.arm64Operand(:quad)}, \##{currentValue}, lsl \##{shift}")
    673722        end
    674723    }
Note: See TracChangeset for help on using the changeset viewer.